【问题标题】:What is a good way to stop all pthreads without using mutex or global variables?在不使用互斥锁或全局变量的情况下停止所有 pthread 的好方法是什么?
【发布时间】:2016-11-08 11:57:27
【问题描述】:

假设您不知道 mutex_locks 并且不允许在程序中使用全局变量,如果返回一个成功的变量,您可以做什么来停止所有正在运行的 pthread?

例如,您有一个传递给 pthread 的数据结构,其中包含:

typedef struct {

    char * string1;         //info from argv[1]
    char * string2;         //info from argv[2]

    int id;                 //thread id

} dataStruct;

在 main.c 中创建 pthread 时,您可以这样创建它们:

dataStruct dataStr[nbThread];           //array of dataStructs for each thread
pthread_t tabThread[nbThread];          //Pointers for thread 1

for (int i = 0; i < nbThread; ++i) {    //initiation of threads...
    dataStr[i].string1 = argv[1];

    pthread_create(&tabThread[i], NULL, thread, (void *)&dataStr[i]);   //create pthreads and
}   

for (int i = 0; i < nbThread; ++i) {
    pthread_join(tabThread[i], (void**)&(ptr[i]));                      //join threads
    //printf("\n return value from thread[%d] is [%d]\n",i, *ptr[i]);
}

现在其中一个线程找到了您希望实现的目标,如何让所有线程同时停止?

我可以在结构中有一个指针指向 main 中的一个变量,一旦线程成功就可以将其更改为 true 吗?

我可以使用 pthread 的返回值以某种方式阻止它们吗?

我有点难以理解这些指针。任何帮助表示赞赏。

【问题讨论】:

  • “停止”如暂停?还是像 kill 中的“停止”?
  • raise(SIGABRT);,或等效的kill(getpid(), SIGABRT);,或等效的pthread_kill(pthread_self(), SIGABRT); 将以您可以定义“同时”的最佳方式停止所有线程。
  • 你确实有一个包含所有线程的数组,遍历它并调用pthread_cancel,并为每个线程传递pthread_t。就像您在加入线程时所做的一样。
  • 只需使用exit 即可退出您的程序。这会结束所有线程,甚至会进行一些清理。
  • exit 终止进程,并根据定义终止进程的所有线程。

标签: c pthreads


【解决方案1】:

这是使用 POSIX 线程完成您希望的事情的方法:

#include <unistd.h>
#include <stdbool.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

#define NUM_THREADS 10

pthread_mutex_t cv_mutex;
pthread_cond_t notification_cv;
pthread_t threads[NUM_THREADS];

bool allfinished = false;

void *worker(void *arg) {
    /* Allow cancellation, */
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    /* and get canceled at any time, not just at cancellation points: */
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

    int loop = 0;

    while (true) {
        printf("iteration %d\n", loop);
        ++loop;

        /* Do some "work": */
        ;

        /* We specifically assume the only shared state
         * is the condition variable, and that the workers
         * share NO OTHER STATE.
         * If they do, adjust the critical section accordingly,
         * perhaps with a 2nd mutex.
         */
        pthread_mutex_lock(&cv_mutex);
        if (loop > 5) { /* Simulate "work end" */
            allfinished = true;
            pthread_cond_broadcast(&notification_cv);
            pthread_mutex_unlock(&cv_mutex);
            pthread_exit(NULL);
        }
        pthread_mutex_unlock(&cv_mutex);
    }
    pthread_exit(NULL);
}

void *master(void *t) {
    /* Lock mutex and wait for signal. */
    pthread_mutex_lock(&cv_mutex);

    /* Loop because a thread might be awoken from its
     * waiting state even though no thread signalled
     * the condition variable:
     */
    while (!allfinished)
        pthread_cond_wait(&notification_cv, &cv_mutex);
    printf("master: woken up.\n");

    /* Unlocking the mutex allows workers to signal the condition variable again,
     * but this is irrelevant as we're heading for end-of-life:
     */
    pthread_mutex_unlock(&cv_mutex);

    for (size_t i = 0; i < NUM_THREADS - 1; ++i) {
        pthread_cancel(threads[i]);
    }

    pthread_exit(NULL);
}

int main(int argc, char *argv[]) {
    pthread_attr_t attr;

    /* Initialize mutex and condition variable objects */
    pthread_mutex_init(&cv_mutex, NULL);
    pthread_cond_init(&notification_cv, NULL);

    /* For portability, explicitly create threads in a joinable state: */
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

    /* Keep a separate `master` thread, which may be doing other bookkeeping: */
    pthread_t s;
    pthread_create(&s, &attr, master, NULL);

    for(size_t i = 0; i < sizeof threads / sizeof *threads; ++i) {
        if (!pthread_create (&threads[i], &attr, worker, NULL) ) {
            printf("worker %zu created\n", i);
        }
    }

    /* Wait for all threads to complete. This will not wait forever because the master
     * thread will cancel all of the workers:
     */
    for (size_t i = 0; i < sizeof threads / sizeof *threads; ++i) {
        pthread_join(threads[i], NULL);
        printf("worker %zu done\n", i);
    }
    pthread_join(s, NULL);
    printf("master done\n");

    /* Clean up and exit */
    pthread_attr_destroy(&attr);
    pthread_mutex_destroy(&cv_mutex);
    pthread_cond_destroy(&notification_cv);
    pthread_exit(NULL);
}

【讨论】:

  • 我非常感谢您花时间回答我的问题@michael,但我的问题明确表示不要使用互斥锁、信号量或全局变量。我们刚刚开始在我的课程中学习 POSIX 线程,所以对于第一个分级程序,我们不应该使用互斥锁/信号量。对于那个程序,我发现的方法效果最好。我们本周开始了线程同步,所以我会看看你的答案。还是谢谢。
【解决方案2】:

我还不能评论,所以我会回复:

我认为您可以简单地使用 main 中定义的布尔值并将其作为引用/指针传递给您的线程。

然后,当您的一个线程结束时,它必须更改此变量值以通知所有其他线程。因此,他们必须在他们的进程循环中检查这个变量的状态才能从中中断。

如果您使用 C11 和 _Atomic 关键字,这些操作在布尔值上应该不存在并发/数据竞争问题(@EOF 强调)。

最后,您的循环调用 pthread_join 将使您的程序等待,直到每个线程都干净地返回。

【讨论】:

  • 您提出的建议称为数据竞赛。这是未定义的行为
  • 我的错,确实它并不完全安全,所以也许在布尔变量上使用 C11 和 _Atomic 关键字?
  • @Wexiwa 我不知道为什么您的解决方案被否决了,但这正是我最终所做的。完美运行。虽然我没有使用布尔值,而是使用了 int。
【解决方案3】:

所以最后我自己找到了答案。它比我想象的要简单得多。

所以我主要创建了一个“标志”变量:

int flag = 0;

然后在我的结构中添加了以下指针:

int * ptrTrouve;        //ptr on address of flag in main.c

然后,在创建所有 pthread 并在参数中传递 pthread_create 结构之前,我让 ptrTrouve 指向标志的地址。我就是这样做的

for (int i = 0; i < nbThread; ++i) {            //initiation of threads...

    dataStr[i].id    = i;                       //thread ID in appropriate struct

    dataStr[i].ptrTrouve = &flag;               //where the value of flag is stored

    pthread_create(&tabThread[i], NULL, thread, (void *)&dataStr[i]);   //create pthreads and
}   

最后在我的线程中,当函数同时运行时,它们会永久验证是否:

*(dataStr.ptrTrouve) == 0

如果其中一个线程成功,它们会像这样更改标志的值:

*(dataStr.ptrTrouve) = 1;

由于每个线程都在不断地监视标志 (*(dataStr.ptrTrouve)) 的值,它们都可以同时运行。一旦标志发生变化,就会发生这种情况:

if(*(dataStr.ptrTrouve) == 1){              //if successful 
    pthread_exit(NULL);
}

我希望这个解决方案可以在未来对其他人有所帮助。

【讨论】:

  • 您的“解决方案”是一场数据竞赛。之前已经注意到了。
  • 鉴于我没有编写核导弹系统的代码,我不认为这是世界末日,我存在数据竞赛问题。它可能不是最好的解决方案,但它是一个解决方案。 @MichaelFoukarakis
  • 不要编写可能有时似乎有效的代码,而是查看我的答案,并明确可靠地解决您的问题。
猜你喜欢
  • 2012-06-05
  • 1970-01-01
  • 2010-09-18
  • 1970-01-01
  • 2016-09-05
  • 1970-01-01
  • 2014-09-26
  • 2020-09-02
  • 2011-02-15
相关资源
最近更新 更多