我在C中创建一个多线程程序,我遇到了一些麻烦。 你有创建线程的函数:
import datetime
last_updated = db.Column(db.DateTime, default=datetime.datetime.now())
这里有我的args结构:
void create_thread(t_game_data *game_data)
{
size_t i;
t_args *args = malloc(sizeof(t_args));
i = 0;
args->game = game_data;
while (i < 10)
{
args->initialized = 0;
args->id = i;
printf("%zu CREATION\n", i);//TODO: Debug
pthread_create(&game_data->object[i]->thread_id, NULL, &do_action, args);
i++;
while (args->initialized == 0)
continue;
}
}
最后,处理创建的线程的函数
typedef struct s_args
{
t_game_data *object;
size_t id;
int initialized;
}args;
问题是:
主线程将比新线程初始化变量更快地创建新线程:
void *do_action(void *v_args)
{
t_args *args;
t_game_data *game;
size_t id;
args = v_args;
game = args->game;
id = args->id;
args->initialized = 1;
[...]
return (NULL);
}
因此,有时,2个不同的线程将从args = v_args;
game = args->game;
id = args->id;
获得相同的id
。
为了解决这个问题,我使用变量args->id
作为bool,因此在新线程初始化期间使主线程“休眠”。
但我认为这真的很有罪。 也许有一种方法可以用互斥量做到这一点?但我听说解锁一个不属于他的线程的互斥锁并不“合法”。
感谢您的回答!
答案 0 :(得分:2)
解决此问题的最简单方法是将不同的t_args
对象传递给每个新线程。为此,在循环内移动分配,并使每个线程负责释放自己的参数struct:
void create_thread(t_game_data *game_data) {
for (size_t i = 0; i < 10; i++) {
t_args *args = malloc(sizeof(t_args));
if (!args) {
/* ... handle allocation error ... */
} else {
args->game = game_data;
args->id = i;
printf("%zu CREATION\n", i);//TODO: Debug
if (pthread_create(&game_data->object[i]->thread_id, NULL,
&do_action, args) != 0) {
// thread creation failed
free(args);
// ...
}
}
}
}
// ...
void *do_action(void *v_args) {
t_args *args = v_args;
t_game_data *game = args->game;
size_t id = args->id;
free(v_args);
args = v_args = NULL;
// ...
return (NULL);
}
但你也写道:
为了解决这个问题,我使用一个初始化为bool的变量,所以make&#34; sleep&#34; 新线程初始化期间的主线程。
但我认为这真的很有罪。也许有办法做到这一点 用互斥量?但我听说它不是合法的&#34;解锁一个互斥锁 不属于他的主题。
如果您仍然希望一个线程等待另一个线程修改某些数据(正如您的原始策略所要求的那样),那么您必须使用原子数据或某种同步对象。您的代码否则包含数据争用,因此具有未定义的行为。在实践中,您不能在原始代码中假设主线程将曾看到新线程写入args->initialized
。 &#34;声色&#34;这是一种不寻常的描述方式,但如果你属于神圣教会,也许是恰当的。
您可以使用互斥锁来解决该问题,只需使用互斥锁保护循环中args->initialized
的测试 - 而不是整个循环 - 并保护线程&#39;使用相同的互斥锁写入该对象,但这是令人讨厌和丑陋的。等待新线程增加信号量(不是繁忙的等待,initialized
变量被信号量替换),或设置并等待条件变量(再次不是忙等待,但仍需要initialized
变量或等效变量。)
答案 1 :(得分:1)
问题是在create_thread
中,您将相同的t_args
结构传递给每个线程。实际上,您可能希望为每个线程创建自己的t_args结构。
发生的事情是你的第一个线程正在启动传递给它的args。在该线程可以运行do_action
之前,循环正在修改args结构。由于thread2和thread1都指向相同的args结构,因此当它们运行do_action
时,它们将具有相同的id。
哦,别忘了不要忘记记忆
答案 2 :(得分:0)
您应该考虑使用条件变量。你可以在http://maxim.int.ru/bookshelf/PthreadsProgram/htm/r_28.html找到一个例子。 基本上在主线程中等待并在其他线程中发出信号。
答案 3 :(得分:0)
除了几个主要问题之外,你的解决方案应该在理论上有效。
args->initiialzed
的更改,或者至少在更晚之后才会看到更改是在另一个尚未刷新回主内存的内核的缓存中你可以使用John Bollinger的解决方案,为每个线程mallocs一组新的args,它很好。唯一的缺点是每个线程创建的malloc / free对。另一种方法是使用像Santosh建议的“适当”同步功能。我可能会考虑这个,除非我使用信号量比条件变量更简单。
信号量是一个原子计数器,有两个操作:等待和信号。如果信号量的值大于零,则等待操作会递减信号量,否则会使线程进入等待状态。信号操作会增加信号量,除非有线程在等待它。如果有,它会唤醒其中一个线程。
因此,解决方案是创建一个初始值为0的信号量,启动线程并等待信号量。然后线程在完成初始化时发信号通知信号量。
#include <semaphore.h>
// other stuff
sem_t semaphore;
void create_thread(t_game_data *game_data)
{
size_t i;
t_args args;
i = 0;
if (sem_init(&semaphore, 0, 0) == -1) // third arg is initial value
{
// error
}
args.game = game_data;
while (i < 10)
{
args.id = i;
printf("%zu CREATION\n", i);//TODO: Debug
pthread_create(&game_data->object[i]->thread_id, NULL, &do_action, args);
sem_wait(&semaphore);
i++;
}
sem_destroy(&semaphore);
}
void *do_action(void *v_args) {
t_args *args = v_args;
t_game_data *game = args->game;
size_t id = args->id;
sem_post(&semaphore);
// Rest of the thread work
return NULL;
}
由于同步,我可以安全地重用args结构,事实上,我甚至不需要malloc它 - 它很小,所以我声明它是函数的本地。
说了这么多,我仍然认为John Bollinger的解决方案对于这个用例更好,但一般都要注意信号量。