C ++,pthreads:如何从多个线程中停止工作线程

时间:2017-05-18 17:59:32

标签: c++ multithreading pthreads

我需要能够阻止单个工作线程继续从任意其他线程中的任意点执行,包括但不限于主线程。我去年制作了我认为正在使用的代码,但是在一些线程死锁之后的今天的调查显示它似乎不能正常工作,特别是关于互斥锁。

代码需要在每次在主线程中调用辅助方法start_path_explorer()时,在工作线程中运行一个特定的方法path_explorer_t :: step()。 start_path_explorer()只能从主线程中调用。

另一个方法,stop_path_explorer()必须能够在任何时候由任何线程(运行path_explorer_t :: step()的线程除外)调用,并且在确定path_explorer_t :: step之前不能返回( )已完全完成。

此外,如果karte_t :: world-> is_terminating_threads()为true,则不得调用path_explorer_t :: step(),而必须在下一次机会时终止该线程。线程不得在其他情况下终止。

我为此编写的代码如下:


    void* path_explorer_threaded(void* args)
    {
        karte_t* world = (karte_t*)args;
        path_explorer_t::allow_path_explorer_on_this_thread = true;
        karte_t::path_explorer_step_progress = 2;

        do
        {
            simthread_barrier_wait(&start_path_explorer_barrier);
            karte_t::path_explorer_step_progress = 0;
            simthread_barrier_wait(&start_path_explorer_barrier);
            pthread_mutex_lock(&path_explorer_mutex);

            if (karte_t::world->is_terminating_threads())
            {
                karte_t::path_explorer_step_progress = 2;
                pthread_mutex_unlock(&path_explorer_mutex);
                break;
            }

            path_explorer_t::step();

            karte_t::path_explorer_step_progress = 1;

            pthread_cond_signal(&path_explorer_conditional_end);
            karte_t::path_explorer_step_progress = 2;
            pthread_mutex_unlock(&path_explorer_mutex);
        } while (!karte_t::world->is_terminating_threads());

        karte_t::path_explorer_step_progress = -1;
        pthread_exit(NULL);
        return args;
    }


    void karte_t::stop_path_explorer()
    {
    #ifdef MULTI_THREAD_PATH_EXPLORER
        pthread_mutex_lock(&path_explorer_mutex);

        if (path_explorer_step_progress = 0)
        {
            pthread_cond_wait(&path_explorer_conditional_end, &path_explorer_mutex);
        }

        pthread_mutex_unlock(&path_explorer_mutex);
    #endif
    }

    void karte_t::start_path_explorer()
    {
    #ifdef MULTI_THREAD_PATH_EXPLORER
        if (path_explorer_step_progress == -1)
        {
            // The threaded path explorer has been terminated, so do not wait
            // or else we will get a thread deadlock.
            return;
        }
        pthread_mutex_lock(&path_explorer_mutex);
        if (path_explorer_step_progress > 0)
        {
            simthread_barrier_wait(&start_path_explorer_barrier);
        }
        if(path_explorer_step_progress > -1)
        {
            simthread_barrier_wait(&start_path_explorer_barrier);
        }
        pthread_mutex_unlock(&path_explorer_mutex);
    #endif 
    }

但是,我发现,由于我不理解的原因,stop_path_explorer()中的互斥锁无法正常工作,并且它不会阻止互斥锁线在path_explorer_threaded中传递,结果是调用stop_path_explorer()的线程可能在cond_wait中等待,而工作线程本身在“do”下面的顶部屏障处等待。它似乎也能够产生互斥锁可以解锁两次的条件,除非我将其设置为递归,否则会产生未定义的行为。

我是否只需要将mutex属性设置为递归并在stop_path_explorer()中的条件语句中添加额外的解锁,还是需要进行更基本的重新设计?如果是后者,有没有人建议如何去做呢?

提前感谢您的帮助。

1 个答案:

答案 0 :(得分:0)

进一步调查后,我认为我对自己的问题有一个潜在的答案。

我误解了pthread_cond_wait()如何与互斥锁一起工作 - 文档说它锁定,而不是解锁传递给它的互斥锁。

这意味着互斥锁从同一个线程中被双重锁定,这会产生未定义的行为,并且可能导致我看到的一些奇怪的问题。

我现在用第二个互斥锁重写代码如下(代码示例中未显示新定义):


    void* path_explorer_threaded(void* args)
    {
        karte_t* world = (karte_t*)args;
        path_explorer_t::allow_path_explorer_on_this_thread = true;
        karte_t::path_explorer_step_progress = 2;
        int mutex_error = 0;

        do
        {
            simthread_barrier_wait(&start_path_explorer_barrier);
            karte_t::path_explorer_step_progress = 0;
            simthread_barrier_wait(&start_path_explorer_barrier);   

            if (karte_t::world->is_terminating_threads())
            {
                karte_t::path_explorer_step_progress = 2;
                break;
            }

            path_explorer_t::step();

            mutex_error = pthread_mutex_lock(&path_explorer_mutex);
            karte_t::path_explorer_step_progress = 1;
            mutex_error = pthread_mutex_unlock(&path_explorer_mutex);

            pthread_cond_signal(&path_explorer_conditional_end);

            mutex_error = pthread_mutex_lock(&path_explorer_mutex);
            karte_t::path_explorer_step_progress = 2;
            mutex_error = pthread_mutex_unlock(&path_explorer_mutex);

        } while (!karte_t::world->is_terminating_threads());

        karte_t::path_explorer_step_progress = -1;
        pthread_exit(NULL);
        return args;
    }


    void karte_t::stop_path_explorer()
    {
    #ifdef MULTI_THREAD_PATH_EXPLORER
        int mutex_error = 0;

        while (path_explorer_step_progress == 0)
        {
            mutex_error = pthread_mutex_lock(&path_explorer_mutex);
            pthread_cond_wait(&path_explorer_conditional_end, &path_explorer_cond_mutex);
            if (&path_explorer_mutex)
            {
                mutex_error = pthread_mutex_unlock(&path_explorer_mutex);
                mutex_error = pthread_mutex_unlock(&path_explorer_cond_mutex);
            }
        }

    #endif
    }

    void karte_t::start_path_explorer()
    {
    #ifdef MULTI_THREAD_PATH_EXPLORER
        if (path_explorer_step_progress == -1)
        {
            // The threaded path explorer has been terminated, so do not wait
            // or else we will get a thread deadlock.
            return;
        }
        if (path_explorer_step_progress > 0)
        {
            simthread_barrier_wait(&start_path_explorer_barrier);
        }
        if(path_explorer_step_progress > -1)
        {
            simthread_barrier_wait(&start_path_explorer_barrier);
        }
    #endif 
    }

但是,我不相信这段代码完全正确。采用这种软件的软件是一种开源计算机游戏,可以使用锁步网络在多玩家配置中通过互联网进行播放(这意味着服务器和客户端必须完全确定地执行定义起点的代码或他们将失去同步)。使用此代码时,客户端最终将与服务器不同步,而他们不会使用原始代码(提供,即服务器和客户端运行相同的可执行文件:我遇到客户端和服务器出现问题当可执行文件被不同地编译时同步,例如GCC和Visual Studio,我怀疑未定义的行为可能是那里的罪魁祸首。

如果有人能够确认我的新代码是否正确或有任何明显的缺陷,我将非常感激。