...
Code Block |
---|
|
#include <mutex>
#include <thread>
class bank_accountBankAccount {
int balance;
public:
std::mutex balance_mutexbalanceMutex;
bank_accountBankAccount() = delete;
explicit bank_accountBankAccount(int initial_amountinitialAmount) : balance(initial_amountinitialAmount) {}
int get_balance() const { return balance; }
void set_balance(int amount) { balance = amount; }
};
int deposit(bank_accountBankAccount *from, bank_accountBankAccount *to, int amount) {
std::lock_guard<std::mutex> from_lock(from->balance_mutex>balanceMutex);
// Not enough balance to transfer.
if (from->get_balance() < amount) {
return -1; // Indicate error
}
std::lock_guard<std::mutex> to_lock(to->balance_mutex>balanceMutex);
from->set_balance(from->get_balance() - amount);
to->set_balance(to->get_balance() + amount);
return 0;
}
void f(bank_accountBankAccount *ba1, bank_accountBankAccount *ba2) {
// Perform the deposits.
std::thread thr1(deposit, ba1, ba2, 100);
std::thread thr2(deposit, ba2, ba1, 100);
thr1.join();
thr2.join();
} |
...
This compliant solution eliminates the circular wait condition by establishing a predefined order for locking in the deposit()
function. Each thread will lock on the basis of the bank_account
BankAccount
ID, which is set when the bank_account
BankAccount
object is initialized.
Code Block |
---|
|
#include <atomic>
#include <mutex>
#include <thread>
class bank_accountBankAccount {
static std::atomic<unsigned int> global_idglobalId;
const unsigned int id;
int balance;
public:
std::mutex balance_mutexbalanceMutex;
bank_accountBankAccount() = delete;
explicit bank_accountBankAccount(int initial_amountinitialAmount) : id(global_idglobalId++), balance(initial_amountinitialAmount) {}
unsigned int get_id() const { return id; }
int get_balance() const { return balance; }
void set_balance(int amount) { balance = amount; }
};
std::atomic<unsigned int> bank_accountBankAccount::global_idglobalId(1);
int deposit(bank_accountBankAccount *from, bank_accountBankAccount *to, int amount) {
std::mutex *first;
std::mutex *second;
if (from->get_id() == to->get_id()) {
return -1; // Indicate error
}
// Ensure proper ordering for locking.
if (from->get_id() < to->get_id()) {
first = &from->balance_mutex>balanceMutex;
second = &to->balance_mutex>balanceMutex;
} else {
first = &to->balance_mutex>balanceMutex;
second = &from->balance_mutex>balanceMutex;
}
std::lock_guard<std::mutex> first_lockfirstLock(*first);
std::lock_guard<std::mutex> second_locksecondLock(*second);
// Check for enough balance to transfer.
if (from->get_balance() >= amount) {
from->set_balance(from->get_balance() - amount);
to->set_balance(to->get_balance() + amount);
return 0;
}
return -1;
}
void f(bank_accountBankAccount *ba1, bank_accountBankAccount *ba2) {
// Perform the deposits.
std::thread thr1(deposit, ba1, ba2, 100);
std::thread thr2(deposit, ba2, ba1, 100);
thr1.join();
thr2.join();
} |
...
Code Block |
---|
|
#include <mutex>
#include <thread>
class bank_accountBankAccount {
int balance;
public:
std::mutex balance_mutexbalanceMutex;
bank_accountBankAccount() = delete;
explicit bank_accountBankAccount(int initial_amountinitialAmount) : balance(initial_amountinitialAmount) {}
int get_balance() const { return balance; }
void set_balance(int amount) { balance = amount; }
};
int deposit(bank_accountBankAccount *from, bank_accountBankAccount *to, int amount) {
// Create lock objects but defer locking them until later.
std::unique_lock<std::mutex> lk1(from->balance_mutex>balanceMutex, std::defer_lock);
std::unique_lock<std::mutex> lk2(to->balance_mutex>balanceMutex, std::defer_lock);
// Lock all of the lock objects simultaneously.
std::lock(lk1, lk2);
if (from->get_balance() >= amount) {
from->set_balance(from->get_balance() - amount);
to->set_balance(to->get_balance() + amount);
return 0;
}
return -1;
}
void f(bank_accountBankAccount *ba1, bank_accountBankAccount *ba2) {
// Perform the deposits.
std::thread thr1(deposit, ba1, ba2, 100);
std::thread thr2(deposit, ba2, ba1, 100);
thr1.join();
thr2.join();
} |
...