问题描述
使用 pthread 库处理小型 C 代码。我对这个代码的要求是先存钱再取钱。
#include <stdio.h>
#include <pthread.h>
int counter = 0;
void *increase();
void *decrease();
int balance = 500;
void *deposit(void*);
void *withdraw(void*);
int main() {
pthread_t t_inc,t_dec;
int dep_money = 300;
int wd_money = 100;
pthread_create(&t_inc,NULL,deposit,&dep_money);
pthread_create(&t_dec,withdraw,&wd_money);
// Wait until the thread is finished
pthread_join(t_inc,NULL);
pthread_join(t_dec,NULL);
}
return 0;
}
// Functions for thread
void *deposit(void *args) {
int *money = (int *)args;
balance += *money;
printf("Deposit: New balance: %d\n",balance);
return NULL;
}
void *withdraw(void *args) {
int *money = (int *)args;
if (balance < *money) {
printf("Not enough balance\n");
} else {
balance -= *money;
printf("Withdraw: New balance: %d\n",balance);
}
return NULL;
}
我在 2 个不同的操作系统上使用了此代码,Ubuntu 和 macOS。由于某种原因,我得到了 2 个不同的结果。
对于 macOS:
存款:新余额:800
提现:新余额:700
对于 Ubuntu:
提现:新余额:400
存款:新余额:700
但是当我在 Ubuntu 中更改 pthread_join() 的顺序时,它按照我想要的顺序工作。
pthread_create(&t_inc,&dep_money);
pthread_join(t_inc,NULL);
pthread_create(&t_dec,&w_money);
pthread_join(t_dec,NULL);
总而言之,我的问题是为什么在 Ubuntu 中运行的第一个代码中它没有作为 pthread_join(t_inc,NULL); 的第一个顺序运行;然后第二个 pthread_join(t_dec,NULL);相反,它运行相反?我不得不在 pthread_create() 之后立即调用 pthread_join() 以使其正常工作,我认为它效率不高,因为我还没有创建第二个线程。
解决方法
这只是一个竞争条件。调用一个线程的pthread_create
先于调用另一个并不能保证在第一的。所以打电话
pthread_create(&t_inc,NULL,deposit,&dep_money);
pthread_create(&t_dec,withdraw,&wd_money);
并不意味着您将在运行 deposit()
的线程之前调用运行 withdraw()
的线程。这完全取决于操作系统何时实际安排它们,作为程序员,不应对此做出任何假设。因此,结果的差异是因为任何一个线程都可能在之前运行。
所以,回答你的问题
我的问题是为什么在 Ubuntu 中运行的第一个代码中它没有运行 pthread_join(t_inc,NULL)的第一个顺序;然后第二个 pthread_join(t_dec,NULL);相反,它运行相反?
不要对线程实际运行的顺序抱有任何期望。
打电话
pthread_create(&t_inc,&dep_money);
pthread_join(t_inc,NULL);
pthread_create(&t_dec,&w_money);
pthread_join(t_dec,NULL);
将始终有效,因为您基本上已经序列化了线程创建,并且调用 deposit()
的线程将始终在运行 withdraw()
的线程产生之前完成,因此它适用于所有操作系统。
编辑:
为了完整性,正如您提到的,创建线程并立即调用 join()
并没有多大用处,因为它与按顺序调用函数 deposit()
和 withdraw()
一样好,考虑到这里的 deposit()
和 withdraw()
并没有真正做很多重要的工作,可能会阻止主线程,阻止它继续执行其他任务。此外,您通常需要处理共享变量。例如如果您只有一个变量 amount
,然后每个线程更新相同的值,一个增加余额,另一个减去余额,那么您可能需要像 mutex
/condition_variable
这样的构造来实现您想要的结果,没有竞争条件。