我有功能display.c:

/* DO NOT EDIT THIS FILE!!!  */

#include <stdio.h>
#include <unistd.h>
#include "display.h"

void display(char *str)
{
    char *p;

    for (p=str; *p; p++)
    {
        write(1, p, 1);
        usleep(100);
    }
}


而display.h是:

/* DO NOT EDIT THIS FILE!!!  */

#ifndef __CEID_OS_DISPLAY_H__
#define __CEID_OS_DISPLAY_H__
void display(char *);
#endif


我的任务是使用pthreads以获得以下输出:

abcd
abcd
abcd
..
..


请注意,我不能编辑文件display.c或文件display.c。我必须使用互斥锁才能成功显示上面显示的输出。

下面的代码块是我最终达到我想要的结果的最接近的尝试:

#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
#include <pthread.h>
#include "display.h"

pthread_t mythread1;
pthread_t mythread2;
pthread_mutex_t m1, m2;

void *ab(void *arg)
{
    pthread_mutex_lock(&m1);
    display("ab");
    pthread_mutex_unlock(&m1);
}

void *cd(void *arg)
{
    pthread_mutex_lock(&m1);
    display("cd\n");
    pthread_mutex_unlock(&m1);
}

int main(int argc, char *argv[])
{
    pthread_mutex_init(&m1, NULL);
    pthread_mutex_init(&m2, NULL);

    int i;

    for(i=0;i<10;i++)
    {
        pthread_create(&mythread1, NULL, ab, NULL);
        pthread_create(&mythread2, NULL, cd, NULL);
    }

    pthread_join(mythread1, NULL);
    pthread_join(mythread2, NULL);
    pthread_mutex_destroy(&m1);
    pthread_mutex_destroy(&m2);
    return EXIT_SUCCESS;
}


上面代码的输出是这样的:

abcd
abcd
abcd
abcd
ababcd
cd
abcd
abcd
abcd
abcd


如您所见,“ ab”和“ cd \ n”从不混合,但是每次我运行代码时,输​​出都会不同。我想确保每次运行此代码时输出为:

abcd
abcd
abcd


十次。

因为我无法从已经知道的事情中找到任何解决方案,所以我真的很坚持。

最佳答案

互斥锁本身无法解决您的问题。它可以防止两个线程同时运行,但是不能强制它们轮流运行。

您可以使用互斥锁之外的条件变量或一对信号量来执行此操作。不管哪种方式,关键是要始终保持对线程转向的感觉。

我自己,我认为信号量方法更易于理解和编码。每个信号量主要与一个不同的线程关联。该线程必须锁定信号量才能继续。完成一次迭代后,它将解锁另一个信号量以允许另一个线程继续运行,然后循环返回以尝试再次锁定其自己的信号量(尚无法执行此操作)。另一个线程的工作方式相同,但是信号灯角色相反。大概是:

sem_t sem1;
sem_t sem2;

// ...

void *thread1_do(void *arg) {
    int result;

    do {
        result = sem_wait(&sem1);
        // do something
        result = sem_post(&sem2);
    } while (!done);
}

void *thread2_do(void *arg) {
    int result;

    do {
        result = sem_wait(&sem2);
        // do something else
        result = sem_post(&sem1);
    } while (!done);
}


为了简洁起见,省略了信号量初始化,错误检查等。

更新后添加:

由于您现在添加了必须使用互斥锁(大概以非平凡的方式),因此下一个最佳方法是引入条件变量(与互斥锁一起使用)和普通的共享变量以跟踪哪个线程打开了它是。然后,每个线程都在等待条件变量以获取互斥量,在互斥量的保护下,该共享变量将检查共享变量以查看是否轮到它了,如果是,则继续。大概是:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int whose_turn = 1;

// ...

void *thread1_do(void *arg) {
    int result;

    result = pthread_mutex_lock(&mutex);
    while (1) {
        if (whose_turn == 1) {
            // do something
            whose_turn = 2;  // it is thread 2's turn next
        }
        // break from the loop if finished
        result = pthread_cond_broadcast(&cond);
        result = pthread_cond_wait(&cond, &mutex);
    }
    result = pthread_mutex_unlock(&mutex);
}

void *thread1_do(void *arg) {
    int result;

    result = pthread_mutex_lock(&mutex);
    while (1) {
        if (whose_turn == 2) {
            // do something else
            whose_turn = 1;  // it is thread 1's turn next
        }
        // break from the loop if finished
        result = pthread_cond_broadcast(&cond);
        result = pthread_cond_wait(&cond, &mutex);
    }
    result = pthread_mutex_unlock(&mutex);
}


为简便起见,再次省略了错误检查。

特别要注意的是,当线程等待条件变量时,它会释放关联的互斥量。从等待返回之前,它需要互斥量。还要注意,每个在每次迭代时检查是否轮到它继续进行。这是必需的,因为可能会因等待条件变量而导致虚假唤醒。

关于c - 内存同步,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/34684874/

10-15 04:54