C 中的 pthread:我不明白为什么 pthread_cond_signal 没有唤醒一个线程
pthread in C : i can't figure why pthread_cond_signal is not waking up one thread
我想要一个由多个线程完成的例程,一旦它们被创建,它们需要在复制它们计算的东西之前完成它们的工作。因此,一旦完成工作,线程就会打开 cond_wait。
现在可以运行的一段代码:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#define MAX_RAYCAST_THREADS 2
typedef struct s_thread_env
{
int id;
pthread_t thread;
int work_done;
void *shared_data;
} t_thread_env;
typedef struct s_shared_data
{
t_thread_env *tab_thread_env;
int max_thread;
int all_work_done;
pthread_mutex_t mutex;
pthread_cond_t cond;
int stop;
} t_shared_data;
void set_threads_again(int id, t_shared_data *shared_data)
{
int i;
shared_data->all_work_done = 0;
i = -1;
while (++i < shared_data->max_thread)
shared_data->tab_thread_env[i].work_done = 0;
//i = -1;
//while (++i < shared_data->max_thread)
//{
//if (i != id)
//{
//printf("cond_signal to thread %i\n", i);
//pthread_cond_signal(&shared_data->cond);
//}
//}
pthread_cond_broadcast(&shared_data->cond);
}
void wait_or_signal(t_thread_env *thread_env, t_shared_data *shared_data)
{
int i;
i = 0;
while (i < shared_data->max_thread && shared_data->tab_thread_env[i].work_done)
i++;
if (i == shared_data->max_thread)
{
printf(" allworkdone sent by thread %i\n", thread_env->id);
//printf(" copy_screenpixels() by thread %i\n", thread_env->id);
set_threads_again(thread_env->id, shared_data);
}
else
{
printf(" thread number %i is waiting for other threads\n", thread_env->id);
pthread_cond_wait(&shared_data->cond, &shared_data->mutex);
printf("ENFIN ! thread number %i woke up from condwait\n", thread_env->id);
}
}
void *routine(void *arg)
{
t_thread_env *thread_env;
t_shared_data *shared_data;
int stop;
thread_env = (t_thread_env *)arg;
shared_data = (t_shared_data *)thread_env->shared_data;
pthread_mutex_lock(&shared_data->mutex);
while (!shared_data->stop)
{
printf("new frame> thread_id = %i, thread_env->work_done = %i\n", thread_env->id, thread_env->work_done);
pthread_mutex_unlock(&shared_data->mutex);
printf(" raycast() in routine thread %i\n", thread_env->id);
pthread_mutex_lock(&shared_data->mutex);
thread_env->work_done++;
wait_or_signal(thread_env, shared_data);
}
pthread_mutex_unlock(&shared_data->mutex);
return (0);
}
void init_thread_env(t_shared_data *shared_data, int i)
{
t_thread_env *thread_env;
thread_env = &shared_data->tab_thread_env[i];
thread_env->id = i;
thread_env->shared_data = shared_data;
if (pthread_create(&thread_env->thread, NULL, routine, thread_env) != 0)
printf("Error pthread_create for %i\n", i);
}
void free_all(t_shared_data *shared_data)
{
int i;
pthread_mutex_lock(&shared_data->mutex);
shared_data->stop = 1;
pthread_mutex_unlock(&shared_data->mutex);
i = -1;
while (++i < shared_data->max_thread)
pthread_join(shared_data->tab_thread_env[i].thread, NULL);
printf("\nEND\n\n");
//free etc
}
int main()
{
int i;
t_shared_data *shared_data;
shared_data = (t_shared_data*)malloc(sizeof(t_shared_data)); // if (!shared data){free etc}
shared_data->max_thread = MAX_RAYCAST_THREADS;
pthread_mutex_init(&shared_data->mutex, 0);
pthread_cond_init(&shared_data->cond, 0);
shared_data->tab_thread_env = (t_thread_env*)malloc(sizeof(t_thread_env) * shared_data->max_thread);
i = -1;
while (++i < shared_data->max_thread)
init_thread_env(shared_data, i);
while (1)
sleep(1); //program is turning
free_all(shared_data);
return (0);
}
正确输出:
new frame> thread_id = 0, thread_env->work_done = 0
raycast() in routine thread 0
thread number 0 is waiting for other threads
new frame> thread_id = 1, thread_env->work_done = 0
raycast() in routine thread 1
allworkdone sent by thread 1
cond_signal to thread 0
new frame> thread_id = 1, thread_env->work_done = 0
ENFIN ! thread number 0 woke up from condwait
new frame> thread_id = 0, thread_env->work_done = 0
raycast() in routine thread 0
thread number 0 is waiting for other threads
raycast() in routine thread 1
allworkdone sent by thread 1
cond_signal to thread 0
new frame> thread_id = 1, thread_env->work_done = 0
ENFIN ! thread number 0 woke up from condwait
new frame> thread_id = 0, thread_env->work_done = 0
raycast() in routine thread 0
thread number 0 is waiting for other threads
raycast() in routine thread 1
感谢您阅读我,祝您有美好的一天!
编辑:我制作了一个更具可读性和可编译性的版本,只有 1 个互斥体(旧版本:https://pastebin.com/4zMyBJi2)。
EDIT2:删除了原 post 的一些部分,并试图避免每次数据竞争,我的代码仍然有一些(因为它仍然不起作用)。但我想我快要开始工作了
EDIT3:好的,现在可以使用了,我编辑了代码。主要问题是我对 shared_data 变量的灾难性使用。
I tried to make my raycasting threads work using 1 call of
pthread_create for each thread (in an initialisation function). Is
it possible to do it?
每次成功调用 pthread_create
都会创建一个线程,因此这是 唯一的 方法。但是不要混淆线程的启动函数和线程本身。可以为 运行 同一个线程函数创建多个线程,但这需要多次调用 pthread_create
,每个线程一个。
I guess it is better (for performances) to do
it in this way (rather than an enormous amount of pthread_create and
pthread_join calls), is this correct?
已经选择使用一定数量的线程来完成某些工作,pthread_create
调用的数量已经确定。如果您有性能问题,那么他们应该关注使用多少线程、它们要执行的工作的详细信息以及它们同步的性质和粒度。
In order to make it happen, the last thread (number n) to finish his
job has to tell the other ones that every thread has finished, so
there is a pthread_cond_wait for the (n - 1) first threads, and the
last thread (number n) calls pthread_cond_signal for each (n - 1)
first ones. Each thread has his own mutex.
这似乎有点矫枉过正,即使您可以使它在技术上是正确的。每个线程都有一个互斥锁没有多大意义,因为 互斥锁只有在不同线程使用同一个 时才有用。但是,使用不同的互斥锁来保护不同的共享资源可能是有意义的。您可能需要其中一些的简历,但不是全部。
这似乎是(至少一个区域)您的代码失败的地方。您的线程未正确同步,因为它们都依赖 不同的 互斥体来保护 相同的 资源。我不清楚你的程序是否有不止一个互斥体的意义,但我会首先减少你的使用量。
我想要一个由多个线程完成的例程,一旦它们被创建,它们需要在复制它们计算的东西之前完成它们的工作。因此,一旦完成工作,线程就会打开 cond_wait。
现在可以运行的一段代码:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#define MAX_RAYCAST_THREADS 2
typedef struct s_thread_env
{
int id;
pthread_t thread;
int work_done;
void *shared_data;
} t_thread_env;
typedef struct s_shared_data
{
t_thread_env *tab_thread_env;
int max_thread;
int all_work_done;
pthread_mutex_t mutex;
pthread_cond_t cond;
int stop;
} t_shared_data;
void set_threads_again(int id, t_shared_data *shared_data)
{
int i;
shared_data->all_work_done = 0;
i = -1;
while (++i < shared_data->max_thread)
shared_data->tab_thread_env[i].work_done = 0;
//i = -1;
//while (++i < shared_data->max_thread)
//{
//if (i != id)
//{
//printf("cond_signal to thread %i\n", i);
//pthread_cond_signal(&shared_data->cond);
//}
//}
pthread_cond_broadcast(&shared_data->cond);
}
void wait_or_signal(t_thread_env *thread_env, t_shared_data *shared_data)
{
int i;
i = 0;
while (i < shared_data->max_thread && shared_data->tab_thread_env[i].work_done)
i++;
if (i == shared_data->max_thread)
{
printf(" allworkdone sent by thread %i\n", thread_env->id);
//printf(" copy_screenpixels() by thread %i\n", thread_env->id);
set_threads_again(thread_env->id, shared_data);
}
else
{
printf(" thread number %i is waiting for other threads\n", thread_env->id);
pthread_cond_wait(&shared_data->cond, &shared_data->mutex);
printf("ENFIN ! thread number %i woke up from condwait\n", thread_env->id);
}
}
void *routine(void *arg)
{
t_thread_env *thread_env;
t_shared_data *shared_data;
int stop;
thread_env = (t_thread_env *)arg;
shared_data = (t_shared_data *)thread_env->shared_data;
pthread_mutex_lock(&shared_data->mutex);
while (!shared_data->stop)
{
printf("new frame> thread_id = %i, thread_env->work_done = %i\n", thread_env->id, thread_env->work_done);
pthread_mutex_unlock(&shared_data->mutex);
printf(" raycast() in routine thread %i\n", thread_env->id);
pthread_mutex_lock(&shared_data->mutex);
thread_env->work_done++;
wait_or_signal(thread_env, shared_data);
}
pthread_mutex_unlock(&shared_data->mutex);
return (0);
}
void init_thread_env(t_shared_data *shared_data, int i)
{
t_thread_env *thread_env;
thread_env = &shared_data->tab_thread_env[i];
thread_env->id = i;
thread_env->shared_data = shared_data;
if (pthread_create(&thread_env->thread, NULL, routine, thread_env) != 0)
printf("Error pthread_create for %i\n", i);
}
void free_all(t_shared_data *shared_data)
{
int i;
pthread_mutex_lock(&shared_data->mutex);
shared_data->stop = 1;
pthread_mutex_unlock(&shared_data->mutex);
i = -1;
while (++i < shared_data->max_thread)
pthread_join(shared_data->tab_thread_env[i].thread, NULL);
printf("\nEND\n\n");
//free etc
}
int main()
{
int i;
t_shared_data *shared_data;
shared_data = (t_shared_data*)malloc(sizeof(t_shared_data)); // if (!shared data){free etc}
shared_data->max_thread = MAX_RAYCAST_THREADS;
pthread_mutex_init(&shared_data->mutex, 0);
pthread_cond_init(&shared_data->cond, 0);
shared_data->tab_thread_env = (t_thread_env*)malloc(sizeof(t_thread_env) * shared_data->max_thread);
i = -1;
while (++i < shared_data->max_thread)
init_thread_env(shared_data, i);
while (1)
sleep(1); //program is turning
free_all(shared_data);
return (0);
}
正确输出:
new frame> thread_id = 0, thread_env->work_done = 0
raycast() in routine thread 0
thread number 0 is waiting for other threads
new frame> thread_id = 1, thread_env->work_done = 0
raycast() in routine thread 1
allworkdone sent by thread 1
cond_signal to thread 0
new frame> thread_id = 1, thread_env->work_done = 0
ENFIN ! thread number 0 woke up from condwait
new frame> thread_id = 0, thread_env->work_done = 0
raycast() in routine thread 0
thread number 0 is waiting for other threads
raycast() in routine thread 1
allworkdone sent by thread 1
cond_signal to thread 0
new frame> thread_id = 1, thread_env->work_done = 0
ENFIN ! thread number 0 woke up from condwait
new frame> thread_id = 0, thread_env->work_done = 0
raycast() in routine thread 0
thread number 0 is waiting for other threads
raycast() in routine thread 1
感谢您阅读我,祝您有美好的一天!
编辑:我制作了一个更具可读性和可编译性的版本,只有 1 个互斥体(旧版本:https://pastebin.com/4zMyBJi2)。
EDIT2:删除了原 post 的一些部分,并试图避免每次数据竞争,我的代码仍然有一些(因为它仍然不起作用)。但我想我快要开始工作了
EDIT3:好的,现在可以使用了,我编辑了代码。主要问题是我对 shared_data 变量的灾难性使用。
I tried to make my raycasting threads work using 1 call of pthread_create for each thread (in an initialisation function). Is it possible to do it?
每次成功调用 pthread_create
都会创建一个线程,因此这是 唯一的 方法。但是不要混淆线程的启动函数和线程本身。可以为 运行 同一个线程函数创建多个线程,但这需要多次调用 pthread_create
,每个线程一个。
I guess it is better (for performances) to do it in this way (rather than an enormous amount of pthread_create and pthread_join calls), is this correct?
已经选择使用一定数量的线程来完成某些工作,pthread_create
调用的数量已经确定。如果您有性能问题,那么他们应该关注使用多少线程、它们要执行的工作的详细信息以及它们同步的性质和粒度。
In order to make it happen, the last thread (number n) to finish his job has to tell the other ones that every thread has finished, so there is a pthread_cond_wait for the (n - 1) first threads, and the last thread (number n) calls pthread_cond_signal for each (n - 1) first ones. Each thread has his own mutex.
这似乎有点矫枉过正,即使您可以使它在技术上是正确的。每个线程都有一个互斥锁没有多大意义,因为 互斥锁只有在不同线程使用同一个 时才有用。但是,使用不同的互斥锁来保护不同的共享资源可能是有意义的。您可能需要其中一些的简历,但不是全部。
这似乎是(至少一个区域)您的代码失败的地方。您的线程未正确同步,因为它们都依赖 不同的 互斥体来保护 相同的 资源。我不清楚你的程序是否有不止一个互斥体的意义,但我会首先减少你的使用量。