Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ruby-on-rails-3/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 主线程和工作线程初始化_C_Multithreading_Pthreads_Libc - Fatal编程技术网

C 主线程和工作线程初始化

C 主线程和工作线程初始化,c,multithreading,pthreads,libc,C,Multithreading,Pthreads,Libc,我正在用C语言创建一个多线程程序,我遇到了一些麻烦。 这里有创建线程的函数: 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;

我正在用C语言创建一个多线程程序,我遇到了一些麻烦。 这里有创建线程的函数:

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;
    }
} 
最后是处理创建的线程的函数

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);
}
问题是:

主线程创建新线程的速度将快于新线程初始化其变量的速度:

args = v_args;
game = args->game;
id = args->id;
因此,有时,两个不同的线程将从
args->id
获得相同的
id
。 为了解决这个问题,我使用变量
initialized
作为bool,以便在新线程初始化期间使“sleep”成为主线程

但我认为这真的是有罪的。 也许有一种方法可以通过互斥来实现这一点?但我听说解锁一个不属于他的线程的互斥锁是不“合法”的


谢谢你的回答

> P>你应该考虑使用条件变量。你可以在这里找到一个例子。
基本上,在主线程中等待,在其他线程中发送信号。

问题在于,在
create\u thread
中,您将相同的
t\u args
结构传递给每个线程。实际上,您可能希望为每个线程创建自己的t_args结构

发生的事情是,您的第一个线程正在启动,并将args传递给它。在该线程可以运行
do_action
之前,循环正在修改args结构。由于thread2和thread1都将指向相同的args结构,因此当它们运行
do_action
时,它们将具有相同的id


哦,别忘了不要泄漏内存

解决这个问题的最简单方法是向每个新线程传递一个不同的
t_args
对象。为此,将分配移动到循环中,并使每个线程负责释放自己的参数结构:

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);
}
void创建线程(游戏数据*游戏数据){
对于(大小i=0;i<10;i++){
t_args*args=malloc(sizeof(t_args));
如果(!args){
/*…处理分配错误*/
}否则{
args->game=game\u数据;
args->id=i;
printf(“%zu创建\n”,i);//TODO:调试
如果(pthread\u create(&game\u data->object[i]->线程id,NULL,
&执行操作,参数)!=0){
//线程创建失败
免费(args);
// ... 
}
}
}
} 
// ...
void*do_动作(void*v_参数){
t_args*args=v_args;
t_game_data*game=args->game;
大小\u t id=args->id;
免费(v_args);
args=v_args=NULL;
// ...
返回(空);
}
但你也写:

为了解决这个问题,我使用了一个初始化为bool的变量,这样就可以“sleep” 新线程初始化期间的主线程

但我认为这真的是有罪的。也许有办法做到这一点 使用互斥锁?但是我听说解锁一个互斥锁是不合法的 不属于他的线

如果您仍然希望一个线程等待另一个线程修改某些数据,正如您最初的策略所要求的那样,那么您必须使用原子数据或某种类型的同步对象。否则,您的代码包含数据竞争,因此具有未定义的行为。实际上,您不能在原始代码中假设主线程将看到新线程写入
args->initialized
。“有罪”是一种不寻常的描述方式,但如果你属于圣C教会,也许是合适的


使用互斥体可以解决这个问题,方法是只使用互斥体保护循环中的
args->initialized
测试,而不是整个循环,并使用相同的互斥体保护线程对该对象的写入,但这既讨厌又难看。最好是等待新线程增加信号量(不是繁忙的等待,并且
初始化的
变量被信号量替换),或者设置并等待条件变量(同样不是繁忙的等待,但是仍然需要
初始化的
变量或等效变量).

除了几个主要问题外,你的解决方案在理论上应该是可行的

  • 主线程将在while循环中旋转,该循环使用CPU周期检查标志(这是最不严重的问题,如果您知道它不需要等待很长时间,则可以正常工作)
  • 对于空循环,编译器优化程序可以满足触发器的要求。他们也常常不知道一个变量可能会被其他线程修改,并在此基础上做出错误的决定
  • 在多核系统上,主线程可能永远看不到对
    args->initiialzed
    的更改,或者如果更改位于另一个尚未刷新回主内存的内核的缓存中,则至少在很久以后才会看到更改
您可以使用JohnBollinger的解决方案,为每个线程mallocs一组新的arg,这很好。唯一不利的一面是每个线程创建的malloc/free对。另一种选择是使用桑托什建议的“适当”同步功能。我可能会考虑这一点,除了使用信号量比条件变量更简单之外。 信号量是具有两个操作的原子计数器:等待和信号。如果信号量的值大于零,wait操作将递减信号量,否则它将使线程进入等待状态。信号操作增加信号量,除非有线程在等待它。如果有,它会唤醒其中一个线程

因此,解决方案是创建一个初始值为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;
} 
#包括
//其他东西
信号量;
无效创建线程(t_游戏数据*游戏数据)
{
尺寸i;
t_args args;
i=0;
if(sem_init(&信号量,0,0)==-1)//第三个参数是初始值
{
//错误
}
args.game=游戏数据;
而(i<10)
{
A.
#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;
}