Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/139.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 什么';对于使用pthread屏障进行同步的线程,干净/可靠地关闭它是一个好策略吗?_C_Pthreads_Pthread Barriers - Fatal编程技术网

C 什么';对于使用pthread屏障进行同步的线程,干净/可靠地关闭它是一个好策略吗?

C 什么';对于使用pthread屏障进行同步的线程,干净/可靠地关闭它是一个好策略吗?,c,pthreads,pthread-barriers,C,Pthreads,Pthread Barriers,我有一个基于pthread的多线程程序,它有四个线程无限期地执行这个运行循环(伪代码): 在第一阶段,每个线程都会更新自己的状态变量,这很好,因为在第一阶段,没有其他线程读取任何其他线程的状态变量。然后在第二阶段,就线程读取彼此的状态而言,它是一个免费的,但这没关系,因为在第二阶段,没有线程修改其局部状态变量,因此它们实际上是只读的 我唯一剩下的问题是,当我的应用程序退出时,如何干净可靠地关闭这些线程?(我所说的“干净可靠”,是指不引入潜在的死锁或争用条件,理想情况下不必发送任何UNIX信号来强

我有一个基于pthread的多线程程序,它有四个线程无限期地执行这个运行循环(伪代码):

在第一阶段,每个线程都会更新自己的状态变量,这很好,因为在第一阶段,没有其他线程读取任何其他线程的状态变量。然后在第二阶段,就线程读取彼此的状态而言,它是一个免费的,但这没关系,因为在第二阶段,没有线程修改其局部状态变量,因此它们实际上是只读的

我唯一剩下的问题是,当我的应用程序退出时,如何干净可靠地关闭这些线程?(我所说的“干净可靠”,是指不引入潜在的死锁或争用条件,理想情况下不必发送任何UNIX信号来强制线程退出pthread_barrier_wait()调用)

我的main()线程当然可以为每个线程将keepRunning设置为false,但是它如何让pthread\u barrier\u wait()为每个线程返回呢?让pthread_barrier_wait()返回的唯一方法是将所有四个线程的执行位置同时放在pthread_barrier_wait()内,但当一些线程可能已经退出时,这很难做到

调用pthread_barrier_destroy()似乎是我想要做的事情,但是当任何线程都可能在屏障上等待时,调用pthread_barrier_destroy()是未定义的行为


这个问题有一个很好的解决方案吗?

在屏障处等待的线程不是问题所在,而是仍在运行
UpdateThis…
docomputions…
的线程将延迟关机。您可以通过定期检查
UpdateThis…
docomputions…
函数中的关机来缩短关机时间

下面是一个可能的解决方案的概要

  • main初始化互斥体
    g\u shutdown\u互斥体
  • main锁定互斥锁
  • main启动线程
  • 线程在周期性地尝试锁定 互斥锁,但由于main已锁定互斥锁,
    trylock
    函数 永远都会失败
  • 关闭时,main将解锁互斥锁
  • 现在
    trylock
    将成功,辅助函数将提前返回
  • 在到达第二个屏障之前,任何成功锁定互斥锁的线程都会设置一个全局变量
    g\u shutdown\u request
  • 通过第二个屏障后,所有线程将在
    g\u shutdown\u request
    中看到相同的值,并做出是否退出的相同决定

所以
while
循环如下所示

while(1)
{
    pthread_barrier_wait(&g_stage_one_barrier);

    UpdateThisThreadsStateVariables();

    if ( pthread_mutex_trylock( &g_shutdown_mutex ) == 0 )
    {
        g_shutdown_requested = true;
        pthread_mutex_unlock( &g_shutdown_mutex );
        break;
    }

    pthread_barrier_wait(&g_stage_two_barrier);

    if ( g_shutdown_requested )
        break;

    DoComputationsThatReadFromAllThreadsStateVariables();
}
void UpdateThisThreadsStateVariables( void )
{
    for ( i = 0;; i++ )
    {
        // check the mutex once every 4000 times through the loop
        if ( (i & 0xfff) == 0 && pthread_mutex_trylock( &g_shutdown_mutex ) == 0 )
        {
            pthread_mutex_unlock( &g_shutdown_mutex );   // abnormal termination
            return; 
        }

        // do the important stuff here

        if ( doneWithTheImportantStuff )    // normal termination
            break;
    }
}
工人函数如下所示

while(1)
{
    pthread_barrier_wait(&g_stage_one_barrier);

    UpdateThisThreadsStateVariables();

    if ( pthread_mutex_trylock( &g_shutdown_mutex ) == 0 )
    {
        g_shutdown_requested = true;
        pthread_mutex_unlock( &g_shutdown_mutex );
        break;
    }

    pthread_barrier_wait(&g_stage_two_barrier);

    if ( g_shutdown_requested )
        break;

    DoComputationsThatReadFromAllThreadsStateVariables();
}
void UpdateThisThreadsStateVariables( void )
{
    for ( i = 0;; i++ )
    {
        // check the mutex once every 4000 times through the loop
        if ( (i & 0xfff) == 0 && pthread_mutex_trylock( &g_shutdown_mutex ) == 0 )
        {
            pthread_mutex_unlock( &g_shutdown_mutex );   // abnormal termination
            return; 
        }

        // do the important stuff here

        if ( doneWithTheImportantStuff )    // normal termination
            break;
    }
}

有两个标志并使用类似于以下的东西应该可以工作:

for (;;)
{
    pthread_barrier_wait(&g_stage_one_barrier);           +
                                                          |
    UpdateThisThreadsStateVariables();                    |
                                                          |
    pthread_mutex_lock(&shutdownMtx);                     | Zone 1
    pendingShutdown = !keepRunning;                       |
    pthread_mutex_unlock(&shutdownMtx);                   |
                                                          |
    pthread_barrier_wait(&g_stage_two_barrier);           +
                                                          |
    if (pendingShutdown)                                  |
        break;                                            | Zone 2
                                                          |
    DoComputationsThatReadFromAllThreadsStateVariables(); |
}
shutdownMtx
也应该保护
keepRunning
的设置,尽管它没有显示出来

逻辑是当
pendingShutdown
设置为
true
时,所有线程都必须在区域1内。(即使只有一些线程看到
keepRunning
false
,这也是正确的,因此在
keepRunning
上进行比赛应该是可以的。)因此,它们都将到达
pthread\u barrier\u wait(&g\u stage\u two\u barrier)
,然后在进入区域2时全部爆发


还可以检查
PTHREAD\u BARRIER\u SERIAL\u THREAD
——它是由
PTHREAD\u BARRIER\u wait()
为一个线程返回的——并且只在该线程中锁定和更新
pendingshutton
,这可以提高性能。

存在需求冲突:障碍语义要求所有线程都在
中才能继续,而关闭要求在执行块之间共享线程时终止(可能在不同的障碍内)

我建议用一个支持extern
cancel
call的自定义实现来替换barrier

示例(可能不运行,但想法…):

初始化:

int custom_barrier_init(custom_barrier_t *barrier, int capacity)
{
   if (NULL == barrier || capacity <= 0)
   {
     errno = EINVAL;
     return -1;
   }
   barrier->capacity = capacity;
   barrier->count = 0;
   barrier->first = NULL;
   return pthread_mutex_init(&barrier->lock, NULL);
   return -1;
}
阻塞等待:

int custom_barrier_wait(custom_barrier_t *barrier)
{
   struct _barrier_entry entry;
   int result;
   pthread_cond_init(&barrier->entry, NULL);
   entry->next = NULL;
   entry->released = false;

   pthread_mutex_lock(&barrier->lock);
   barrier->count++;
   if (barrier->count == barrier->capacity)
   {
     _custom_barrier_flush(barrier);
     result = 0;
   }
   else
   {
     entry->next = barrier->first;
     barrier->first = entry;
     while (true)
     {
       pthread_cond_wait(&entry->cond, &barrier->lock);
       if (entry->released)
       {
         result = 0;
         break;
       }
       if (barrier->capacity < 0)
       {
         errno = ECANCELLED;
         result = -1;
         break;
       }
     }
   }
   pthread_mutex_unlock(&barrier->lock);
   pthread_cond_destroy(&entry->cond);
   return result;
}

因此线程代码可以在循环中运行,直到在调用
custom\u barrier\u wait
之后它被取消
error。

您可以有一个额外的线程在相同的barrier上同步,但只作为“shutdown master”存在。您的工作线程将使用问题中的确切代码,“关机主线程”将执行以下操作:

while (keepRunning)
{
    pthread_barrier_wait(&g_stage_one_barrier);

    pthread_mutex_lock(&mkr_lock);
    if (!mainKeepRunning)
        keepRunning = 0;
    pthread_mutex_unlock(&mkr_lock);

    pthread_barrier_wait(&g_stage_two_barrier);
}
当主线程希望其他线程关闭时,它只需执行以下操作:

pthread_mutex_lock(&mkr_lock);
mainKeepRunning = 0;
pthread_mutex_unlock(&mkr_lock);
(即,
keepRunning
变量成为共享线程状态的一部分,该状态在第2阶段为只读,在第1阶段为关闭主线程所有)


当然,您也可以选择其他线程中的一个作为“关机主线程”,而不是使用专用线程作为“关机主线程”。

每个线程在每个循环上等待的两个“等待”事件是什么?也许main()函数只需重置“keeprunning”变量,并重复触发每个“wait”事件。当一个线程设法通过锁定测试后,
main
解锁互斥锁时,此代码会死锁,而其他线程则不会。@ValeriAtamaniouk任何通过锁定测试的线程都会立即解锁互斥锁,因此,不存在僵局。也许这在大纲中并不清楚,但在代码中应该是清楚的。
mutex
只是给线程的一个信号,它们应该关闭(而不是
keepRunning
变量)。没有一个线程将保持互斥锁锁定。但是,如果总共有4个线程,3个线程未能锁定互斥锁并继续,一个线程锁定互斥锁,那么3个线程将在第一个屏障块内无限期等待结束,一个线程将退出。@ValeriAtamaniouk Y
while (keepRunning)
{
    pthread_barrier_wait(&g_stage_one_barrier);

    pthread_mutex_lock(&mkr_lock);
    if (!mainKeepRunning)
        keepRunning = 0;
    pthread_mutex_unlock(&mkr_lock);

    pthread_barrier_wait(&g_stage_two_barrier);
}
pthread_mutex_lock(&mkr_lock);
mainKeepRunning = 0;
pthread_mutex_unlock(&mkr_lock);