问题在于:
我想编写一个创建10个线程的短程序,每个程序都打印一个通过指针传递给线程函数的“id”。
该计划的完整代码如下:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
struct params {
pthread_mutex_t mutex;
int id;
};
typedef struct params params_t;
void* hello(void* arg){
int id;
pthread_mutex_lock(&(*(params_t*)(arg)).mutex);
id = (*(params_t*)(arg)).id;
pthread_mutex_unlock(&(*(params_t*)(arg)).mutex);
printf("Hello from %d\n", id);
}
int main() {
pthread_t threads[10];
params_t params;
pthread_mutex_init (¶ms.mutex , NULL);
int i;
for(i = 0; i < 10; i++) {
params.id = i;
if(pthread_create(&threads[i], NULL, hello, ¶ms));
}
for(i = 0; i < 10; i++) {
pthread_join(threads[i], NULL);
}
return 0;
}
假定的输出是(按此顺序不必要):
Hello from 0
....
Hello from 9
实际结果是:
Hello from 2
Hello from 3
Hello from 3
Hello from 4
Hello from 5
Hello from 6
Hello from 8
Hello from 9
Hello from 9
Hello from 9
我尝试将互斥锁放在hello()
函数的不同位置,但它没有帮助。
我应该如何实现线程同步?
编辑:假定的结果不是必需的0 ... 9它可以是这些数字的任意组合,但每个数字只能出现一次。
答案 0 :(得分:7)
问题在于以下代码:
for(i = 0; i < 10; i++)
{
params.id = i;
if(pthread_create(&threads[i], NULL, hello, ¶ms));
}
你的params.id值在主线程中不断更新,而你将相同的指针传递给所有线程。
请通过动态分配params为params创建单独的内存并将其传递给不同的线程来解决问题。
<强> EDIT1:强> 您使用互斥锁进行保护也是一个错误的想法。虽然你的互斥锁如果在主设备中使用时也可以设置id,可能会使更新互斥,但你可能无法获得所需的输出。不是在不同的线程中从0 ... 9获取值,而是可以获得所有9个或多个线程可以打印相同的值。
因此,对于您期望的输出,使用线程同步并不是一个好主意。如果你仍然需要在所有线程之间使用一个param变量并从每个线程获得0到9的输出,那么最好将pthread_join移动到第一个循环中。这将确保每个线程都被创建,打印该值,然后在main生成下一个线程之前返回。在这种情况下,您也不需要互斥锁。
<强> EDIT2:强> 至于更新的问题,在被问及没有必要按序列打印数字0..9的情况下,打印可以是随机的,但只有一次,问题仍然或多或少地保持不变。
现在,让我们说,params.id的值是0,线程0已经创建,现在,线程0必须在主线程更新之前打印它,否则,当线程0访问它时,值为0 params.id将成为1,你永远不会得到你独特的价值观。那么,如何确保线程0在main更新之前打印它,有两种方式:
老实说,你选择了错误的学习同步问题。共享内存。你可以尝试一些像“生产者 - 消费者”这样的好问题,你真的需要同步工作。
答案 1 :(得分:2)
有两个问题:
一个。您使用的是lock
,但main
并未发现此锁定。
B中。在这种情况下,lock
是不够的。你想要的是让线程通过相互通信来协作(因为你希望main
不增加变量,直到线程说它完成打印它为止。您可以使用pthread_cond_t
来实现此目的(Look here以了解有关此内容的更多信息)。这归结为以下代码(基本上,我在您的代码中添加了pthread_cond_t
的适当用法,以及一堆解释正在发生的事情的评论):
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
struct params {
pthread_mutex_t mutex;
pthread_cond_t done;
int id;
};
typedef struct params params_t;
void* hello(void* arg){
int id;
/* Lock. */
pthread_mutex_lock(&(*(params_t*)(arg)).mutex);
/* Work. */
id = (*(params_t*)(arg)).id;
printf("Hello from %d\n", id);
/* Unlock and signal completion. */
pthread_mutex_unlock(&(*(params_t*)(arg)).mutex);
pthread_cond_signal (&(*(params_t*)(arg)).done);
/* After signalling `main`, the thread could actually
go on to do more work in parallel. */
}
int main() {
pthread_t threads[10];
params_t params;
pthread_mutex_init (¶ms.mutex , NULL);
pthread_cond_init (¶ms.done, NULL);
/* Obtain a lock on the parameter. */
pthread_mutex_lock (¶ms.mutex);
int i;
for(i = 0; i < 10; i++) {
/* Change the parameter (I own it). */
params.id = i;
/* Spawn a thread. */
pthread_create(&threads[i], NULL, hello, ¶ms);
/* Give up the lock, wait till thread is 'done',
then reacquire the lock. */
pthread_cond_wait (¶ms.done, ¶ms.mutex);
}
for(i = 0; i < 10; i++) {
pthread_join(threads[i], NULL);
}
/* Destroy all synchronization primitives. */
pthread_mutex_destroy (¶ms.mutex);
pthread_cond_destroy (¶ms.done);
return 0;
}
我看到您正在尝试的示例是一个可能了解POSIX线程库的玩具程序。在现实世界中,我们都知道,即使不使用线程,也可以更快地完成。但你已经知道了。
答案 2 :(得分:1)
问题是你正在修改主要的params.id“unprotected”。主要的修改也需要互斥保护。您可以通过创建getId()和setId()函数来本地化来保护此访问,这些函数将锁定互斥锁并保护对id的访问,如下所示。这很可能仍然会报告问题,因为根据线程调用getData()的时间,它将有一个或另一个值。所以要解决这个问题,你可以添加一个incrementId()函数并从hello()函数中调用它。
struct params {
pthread_mutex_t mutex;
int id;
};
typedef struct params params_t;
int getId(params_t *p)
{
int id;
pthread_mutex_lock(&(p->mutex));
id = p->id;
pthread_mutex_unlock(&(p->mutex));
return id;
}
void setId(params_t *p, int val)
{
pthread_mutex_lock(&(p->mutex));
p->id = val;
pthread_mutex_unlock(&(p->mutex));
}
void incrementId(params_t *p)
{
pthread_mutex_lock(&(p->mutex));
p->id++;
pthread_mutex_unlock(&(p->mutex));
}
void* hello(void* arg){
params_t *p = (params_t*)(arg);
incrementId(p);
int id = getId(p);
// This could possibly be quite messy since it
// could print the data for multiple threads at once
printf("Hello from %d\n", id);
}
int main() {
pthread_t threads[10];
params_t params;
params.id = 0;
pthread_mutex_init (¶ms.mutex , NULL);
int i;
for(i = 0; i < 10; i++) {
if(pthread_create(&threads[i], NULL, hello, ¶ms));
}
for(i = 0; i < 10; i++) {
pthread_join(threads[i], NULL);
}
return 0;
}
获取唯一线程ID的更好方法是按如下方式定义hello方法:
void* hello(void* arg){
pthread_t threadId = pthread_self();
printf("Hello from %d\n", threadId);
}
为避免所有线程一次尝试打印出现问题,您可以执行以下操作:
void* hello(void* arg){
params_t *p = (params_t*)(arg);
pthread_mutex_lock(&(p->mutex));
p->id++;
int id = p->id;
printf("Hello from %d\n", id);
pthread_mutex_unlock(&(p->mutex));
}
答案 3 :(得分:0)
获得所需输出的最简单方法是修改main
函数,如下所示:
int main() {
pthread_t threads[10];
params_t params;
pthread_mutex_init (¶ms.mutex , NULL);
int i;
for(i = 0; i < 10; i++) {
params.id = i;
if(pthread_create(&threads[i], NULL, hello, ¶ms));
pthread_join(threads[i], NULL); //wait for thread to finish
}
/*for(i = 0; i < 10; i++) {
pthread_join(threads[i], NULL);
}*/
return 0;
}
输出将是:
Hello from 0
...
Hello from 9
编辑:这是纠正问题的同步:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
struct params {
pthread_mutex_t* mutex;
int id;
};
typedef struct params params_t;
void* hello(void* arg){
int id = 0;
params_t* params = (params_t*)arg;
if(params != 0)
{
id = params->id;
delete params;
params = 0;
}
printf("Hello from %d\n", id);
}
int main() {
pthread_t threads[10];
params_t* params = 0;
pthread_mutex_t main_mutex;
pthread_mutex_init (&main_mutex , NULL);
int i;
for(i = 0; i < 10; i++) {
params = new params_t(); //create copy of the id to pass to each thread -> each thread will have it's own copy of the id
params->id = i;
params->mutex = &main_mutex;
if(pthread_create(&threads[i], NULL, hello, params));
}
for(i = 0; i < 10; i++) {
pthread_join(threads[i], NULL);
}
return 0;
}
每个线程必须拥有自己的id副本,以便其他线程在打印之前不会修改id。
答案 4 :(得分:0)
我只是把这个放在这里为这个问题提供另一种解决方案 - 这个问题不涉及互斥 - 没有同步 - 没有条件等。
主要的不同之处在于我们使用pthread_detach
在完成后自动释放线程的资源。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define NUMTHREADS 10
typedef struct params {
int id;
} params_t;
void* hello(void *arg)
{
params_t *p = (params_t*)arg;
int status;
status = pthread_detach(pthread_self());
if (status !=0 )
{
printf("detaching thread\n");
abort();
}
printf("Hello from %d\n", p->id);
free(p);
return NULL;
}
int main()
{
pthread_t thread;
params_t *par;
int i, status;
for (i=0; i<NUMTHREADS; i++)
{
par = (params_t*)malloc(sizeof(params_t));
if (par == NULL)
{
printf("allocating params_t");
abort();
}
par->id = i;
status = pthread_create(&thread, NULL, hello, par);
if (status != 0)
exit(1);
}
/* DO some more work ...*/
sleep(3);
exit(0);
}