带线程的银行账户转账

Bank account transfer with threads

我必须使用线程进行一些银行账户转账并对不同的结果进行基准测试。我认为带有通用锁的同步解决方案的时间必须比每个帐户一个锁的解决方案差。

这是我使用通用锁的实现:

pthread_mutex_t general_mutex;

typedef struct {
    int id;
    double balance;
} Account;

int NThreads = 400; /*Threads number*/

#define N 20             /*Accounts number*/
Account accounts[N];
void transfer(double money, Account* origin, Account* destini) {

    pthread_mutex_lock(&general_mutex); //Init general lock.

    bool wasPosible = withdraw(origin, money);
    if (wasPosible ) deposit(destini, money);

    pthread_mutex_unlock(&general_mutex); //End general lock.
}

这是每个帐户单独锁定的实现:

typedef struct {
    int id;
    double balance;
    pthread_mutex_t mutex; // lock to use/modify vars
} Account;

int NThreads = 400; /*Threads number*/

#define N 20             /*Accounts number*/
Account accounts[N];
void transfer(double money, Account* origin, Account* destini) {

    if (from->id < to->id) {
         pthread_mutex_lock(&(origin->mutex));
         pthread_mutex_lock(&(destini->mutex));
    } else {
         pthread_mutex_lock(&(destini->mutex));
         pthread_mutex_lock(&(origin->mutex));
    }

    bool wasPosible = withdraw(origin, money);
    if (wasPosible ) deposit(destini, amount);

    pthread_mutex_unlock(&(origin->mutex));
    pthread_mutex_unlock(&(destini->mutex));
}

为什么一般的锁解决方案比第二种解决方案花费的时间少?

谢谢

锁定操作不是免费的。在第二个示例中,您执行的 locking/unlocking 操作是第一个示例中的两倍。其他的操作好像是简单的内存访问,应该不会持续很长时间。

我的看法是,在您的系统中,花在锁上的时间比花在实际处理上的时间要多,因此增加锁的数量是无关紧要的。如果 transfer 使用磁盘或网络等慢速 io,情况可能会有所不同。

顺便说一句,正如您在评论中所说,400 个线程的效率可能再次低于更小的线程数。经验法则与您将使用的核心数量一样多,如果处理花费时间等待 io,则增加一个可变因子 - 如果没有 io 永远不会超过可用核心。上限是所有线程使用的内存不得超过您要使用的内存,并且启动和同步线程的开销远低于总处理时间。