gpt4 book ai didi

c - 试图理解 pthread_cond_lock 和 pthread_cond_signal

转载 作者:太空宇宙 更新时间:2023-11-04 06:49:55 25 4
gpt4 key购买 nike

所以我试图准确地理解 pthread_mutex_lock 是如何工作的。

我目前的理解是它解锁了互斥锁并将任何线程通过它进入休眠状态。 sleep 意味着线程处于非事件状态并且不消耗任何资源。

然后它等待信号从休眠状态变为阻塞状态,这意味着线程不能再更改任何变量。

thread 1:
pthread_mutex_lock(&mutex);
while (!condition){
printf("Thread wating.\n");
pthread_cond_wait(&cond, &mutex);
printf("Thread awakened.\n");
fflush(stdout);
}
pthread_mutex_unlock(&mutex);

pthread_cond_signal(&condVar);
pthread_mutex_unlock(&mutex);

所以基本上在上面的示例中,循环运行并运行,每次迭代 pthread_cond_wait检查循环的条件是否为真。如果是,那么 cond_signal被发送并且线程被阻塞,因此它不能再操作任何数据。

我真的很难解决这个问题,我很感激一些关于它是如何工作的输入和反馈,以及我是否开始根据上面的内容理解这一点。

我已经看完了 this发布但我仍然遇到问题

最佳答案

首先,总结一下:

  • pthread_mutex_lock(&mutex) :

    如果 mutex是免费的,然后这个线程立即捕获它。

    如果 mutex被抓取,然后这个线程一直等到 mutex变得自由,然后捕获它。
  • pthread_mutex_trylock(&mutex) :

    如果 mutex是免费的,然后这个线程捕获它。

    如果 mutex被抓取,然后调用立即返回 EBUSY .
  • pthread_mutex_unlock(&mutex) :

    发布 mutex .
  • pthread_cond_signal(&cond) :

    唤醒一个等待条件变量的线程cond .
  • pthread_cond_broadcast(&cond) :

    唤醒所有等待条件变量 cond 的线程.
  • pthread_cond_wait(&cond, &mutex) :

    这必须用 mutex 调用捕获了。

    调用线程将暂时释放mutex并等待cond .

    cond正在广播,或发出信号,并且该线程恰好是被唤醒的线程,那么调用线程将首先重新获取mutex ,然后从通话中返回。

    需要注意的是,在任何时候,调用线程要么有 mutex已抢到,或正在等待cond .中间没有间隔。


  • 让我们看一个实际运行的示例代码。我们将按照 OP 的代码行创建它。

    首先,我们将使用一个结构来保存每个工作函数的参数。由于我们希望在线程之间共享互斥锁和条件变量,因此我们将使用指针。
    #define  _POSIX_C_SOURCE  200809L
    #include <stdlib.h>
    #include <pthread.h>
    #include <limits.h>
    #include <string.h>
    #include <stdio.h>
    #include <errno.h>

    /* Worker function work. */
    struct work {
    pthread_t thread_id;
    pthread_mutex_t *lock; /* Pointer to the mutex to use */
    pthread_cond_t *wait; /* Pointer to the condition variable to use */
    volatile int *done; /* Pointer to the flag to check */
    FILE *out; /* Stream to output to */
    long id; /* Identity of this thread */
    unsigned long count; /* Number of times this thread iterated. */
    };

    线程 worker 函数接收指向上述结构的指针。每个线程迭代循环一次,然后等待条件变量。当被唤醒时,如果完成标志仍然为零,则线程迭代循环。否则,线程退出。
    /* Example worker function. */
    void *worker(void *workptr)
    {
    struct work *const work = workptr;

    pthread_mutex_lock(work->lock);

    /* Loop as long as *done == 0: */
    while (!*(work->done)) {
    /* *(work->lock) is ours at this point. */

    /* This is a new iteration. */
    work->count++;

    /* Do the work. */
    fprintf(work->out, "Thread %ld iteration %lu\n", work->id, work->count);
    fflush(work->out);

    /* Wait for wakeup. */
    pthread_cond_wait(work->wait, work->lock);
    }

    /* *(work->lock) is still ours, but we've been told that all work is done already. */
    /* Release the mutex and be done. */
    pthread_mutex_unlock(work->lock);
    return NULL;
    }

    要运行上述代码,我们还需要一个 main():
    #ifndef  THREADS
    #define THREADS 4
    #endif

    int main(void)
    {
    pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t wait = PTHREAD_COND_INITIALIZER;
    volatile int done = 0;
    struct work w[THREADS];

    char *line = NULL, *p;
    size_t size = 0;
    ssize_t len = 0;

    unsigned long total;
    pthread_attr_t attrs;
    int i, err;

    /* The worker functions require very little stack, but the default stack
    size is huge. Limit that, to reduce the (virtual) memory use. */
    pthread_attr_init(&attrs);
    pthread_attr_setstacksize(&attrs, 2 * PTHREAD_STACK_MIN);

    /* Grab the mutex so the threads will have to wait to grab it. */
    pthread_mutex_lock(&lock);

    /* Create THREADS worker threads. */
    for (i = 0; i < THREADS; i++) {

    /* All threads use the same mutex, condition variable, and done flag. */
    w[i].lock = &lock;
    w[i].wait = &wait;
    w[i].done = &done;

    /* All threads output to standard output. */
    w[i].out = stdout;

    /* The rest of the fields are thread-specific. */
    w[i].id = i + 1;
    w[i].count = 0;

    err = pthread_create(&(w[i].thread_id), &attrs, worker, (void *)&(w[i]));
    if (err) {
    fprintf(stderr, "Cannot create thread %d of %d: %s.\n", i+1, THREADS, strerror(errno));
    exit(EXIT_FAILURE); /* Exits the entire process, killing any other threads as well. */
    }
    }

    fprintf(stderr, "The first character on each line controls the type of event:\n");
    fprintf(stderr, " e, q exit\n");
    fprintf(stderr, " s signal\n");
    fprintf(stderr, " b broadcast\n");
    fflush(stderr);

    /* Let each thread grab the mutex now. */
    pthread_mutex_unlock(&lock);

    while (1) {
    len = getline(&line, &size, stdin);
    if (len < 1)
    break;

    /* Find the first character on the line, ignoring leading whitespace. */
    p = line;
    while ((p < line + len) && (*p == '\0' || *p == '\t' || *p == '\n' ||
    *p == '\v' || *p == '\f' || *p == '\r' || *p == ' '))
    p++;

    /* Do the operation mentioned */
    if (*p == 'e' || *p == 'E' || *p == 'q' || *p == 'Q')
    break;
    else
    if (*p == 's' || *p == 'S')
    pthread_cond_signal(&wait);
    else
    if (*p == 'b' || *p == 'B')
    pthread_cond_broadcast(&wait);
    }

    /* It is time for the worker threads to be done. */
    pthread_mutex_lock(&lock);
    done = 1;
    pthread_mutex_unlock(&lock);

    /* To ensure all threads see the state of that flag,
    we wake up all threads by broadcasting on the condition variable. */
    pthread_cond_broadcast(&wait);

    /* Reap all threds. */
    for (i = 0; i < THREADS; i++)
    pthread_join(w[i].thread_id, NULL);

    /* Output the thread statistics. */
    total = 0;
    for (i = 0; i < THREADS; i++) {
    total += w[i].count;
    fprintf(stderr, "Thread %ld: %lu events.\n", w[i].id, w[i].count);
    }
    fprintf(stderr, "Total: %lu events.\n", total);

    return EXIT_SUCCESS;
    }

    如果将以上内容另存为 example.c ,可以编译成 example使用例如 gcc -Wall -O2 example.c -lpthread -o example .

    要正确直观地掌握操作,请在终端中运行示例,并在其旁边的窗口中显示源代码,并在您提供输入时查看执行进展情况。

    您甚至可以运行 printf '%s\n' s s s b q | ./example 之类的命令快速连续运行一系列事件,或 printf 's\ns\ns\nb\nq\n' | ./example事件之间的时间更短。

    经过一些实验,您会希望发现并非所有输入事件都会导致它们各自的 Action 。这是因为退出事件(上面的 q)不是同步的:它不等待所有待处理的工作完成,而是告诉线程立即退出。这就是为什么即使对于完全相同的输入,事件的数量也可能会有所不同。

    (此外,如果您在条件变量上发出信号并立即对其进行广播,则线程往往只会被唤醒一次。)

    您可以通过延迟退出来缓解这种情况,例如使用 (printf '%s\n' s s b s s s ; sleep 1 ; printf 'q\n' ) | ./example .

    但是,还有更好的方法。条件变量不适用于可数事件;它真的很像旗帜。信号量会更好,但是你应该小心不要溢出信号量;它只能从 0 到 SEM_VALUE_MAX , 包括的。 (因此,您可以使用信号量来表示待处理作业的数量,但可能不是每个/所有线程工作人员完成的迭代次数。)以线程池方式执行工作的队列是最常见的方法。

    关于c - 试图理解 pthread_cond_lock 和 pthread_cond_signal,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52960662/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com