C++ 使用信号量代替忙等待

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13558493/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 17:29:00  来源:igfitidea点击:

C++ Using semaphores instead of busy waiting

c++multithreadingpthreadsposixsemaphore

提问by user1378863

I am attempting to learn about semaphores and multi-threading. The example I am working with creates 1 to t threads with each thread pointing to the next and the last thread pointing to the first thread. This program allows each thread to sequentially take a turn until all threads have taken n turns. That is when the program ends. The only problem is in the tFunc function, I am busy waiting until it is a specific thread's turn. I want to know how to use semaphores in order to make all the threads go to sleep and waking up a thread only when it is its turn to execute to improve efficiency.

我正在尝试了解信号量和多线程。我正在使用的示例创建 1 到 t 个线程,每个线程指向下一个线程,最后一个线程指向第一个线程。这个程序允许每个线程依次轮流,直到所有线程都轮流了 n 次。那是程序结束的时候。唯一的问题是在 tFunc 函数中,我正忙着等待轮到特定线程。我想知道如何使用信号量使所有线程进入睡眠状态并仅在轮到执行时唤醒线程以提高效率。

int turn = 1;
int counter = 0;
int t, n;

struct tData {
        int me;
        int next;
};

void *tFunc(void *arg) {
        struct tData *data;
        data = (struct tData *) arg;
        for (int i = 0; i < n; i++) {
            while (turn != data->me) {
        }
        counter++;
        turn = data->next;
    }
}

int main (int argc, char *argv[]) {
    t = atoi(argv[1]);
    n = atoi(argv[2]);
    struct tData td[t];
    pthread_t threads[t];
    int rc;

    for (int i = 1; i <= t; i++) {
        if (i == t) {
            td[i].me = i;
            td[i].next = 1;
        }
        else {
            td[i].me = i;
            td[i].next = i + 1;
        }
        rc = pthread_create(&threads[i], NULL, tFunc, (void *)&td[i]);
        if (rc) {
            cout << "Error: Unable to create thread, " << rc << endl;
            exit(-1);
        }
    }
    for (int i = 1; i <= t; i++) {
        pthread_join(threads[i], NULL);
    }
    pthread_exit(NULL);
}

回答by David Schwartz

Uses mutexes and condition variables. Here's a working example:

使用互斥锁和条件变量。这是一个工作示例:

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

int turn = 1;
int counter = 0;
int t, n;

struct tData {
        int me;
        int next;
};

pthread_mutex_t mutex;
pthread_cond_t cond;

void *tFunc(void *arg)
{
    struct tData *data;
    data = (struct tData *) arg;
    pthread_mutex_lock(&mutex);
    for (int i = 0; i < n; i++)
    {
        while (turn != data->me)
            pthread_cond_wait(&cond, &mutex);
        counter++;
        turn = data->next;
        printf("%d goes (turn %d of %d), %d next\n", data->me, i+1, n, turn);
        pthread_cond_broadcast(&cond);
    }
    pthread_mutex_unlock(&mutex);
}

int main (int argc, char *argv[]) {
    t = atoi(argv[1]);
    n = atoi(argv[2]);
    struct tData td[t + 1];
    pthread_t threads[t + 1];
    int rc;

    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);

    for (int i = 1; i <= t; i++)
    {
        td[i].me = i;
        if (i == t)
            td[i].next = 1;
        else
            td[i].next = i + 1;

        rc = pthread_create(&threads[i], NULL, tFunc, (void *)&td[i]);
        if (rc)
        {
            printf("Error: Unable to create thread: %d\n", rc);
            exit(-1);
        }
    }
    void *ret;
    for (int i = 1; i <= t; i++)
        pthread_join(threads[i], &ret);
}

回答by chill

Use N+1semaphores. On startup, thread iwaits on semaphore i. When woken up it "takes a turnand signals semaphorei + 1`.

使用N+1信号量。启动时,线程i等待 semaphore i。当它被唤醒时“轮流and signals semaphorei + 1`。

The main thread spawns the N, threads, signals semaphore 0 and waits on semaphore N.

主线程产生N, 线程, 信号 semaphore 0 并等待 semaphore N

Pseudo code:

伪代码:

sem s[N+1];

thread_proc (i):
  repeat N:
      wait (s [i])
      do_work ()
      signal (s [i+1])

main():
  for i in 0 .. N:
    spawn (thread_proc, i)

  repeat N:    
      signal (s [0]);
      wait (s [N]);

回答by tmyklebu

Have one semaphore per thread. Have each thread waiton its semaphore, retrying if sem_waitreturns EINTR. Once it's done with its work, have it postto the next thread's semaphore. This avoids the "thundering herd" behaviour of David's solution by waking only one thread at a time.

每个线程有一个信号量。将每个线程wait放在其信号量上,如果sem_wait返回则重试EINTR。一旦它完成了它的工作,就让它post进入下一个线程的信号量。通过一次只唤醒一个线程,这避免了 David 解决方案的“雷霆万钧”行为。

Also notice that, since your semaphores will never have a value larger than one, you can use a pthread_mutex_tfor this.

另请注意,由于您的信号量的值永远不会大于 1,因此您可以使用 a pthread_mutex_t