关于并行POSIX线程

时间:2013-12-16 21:44:20

标签: c multithreading posix

我想创建两个线程,它们应该是这样的:

P1:

while(1) {
    printf("1");
    printf("2");
    printf("3");
    printf("4");
}
return NULL;

P2:

while(1) {
    printf("5");
    printf("6");
    printf("7");
    printf("8");
}
return NULL;

根据我对并行线程的了解,它不会打印12345678,而是由于缺乏同步而导致数字完全随机变化。

然而,当我尝试在实际代码中复制它时,它会继续打印1234几次,然后切换到5678,打印几次并返回到1234.

我对线程的理解是错误的还是我的代码不等同于问题?

void *print1(void *arg) {
    while(1) {
        printf("1");
        printf("2");
        printf("3");
        printf("4\n");
    }
    return NULL;
}

void *print2(void *arg) {
    while(1){
        printf("5");
        printf("6");
        printf("7");
        printf("8\n");
    }
    return NULL;
}


int main() {
    pthread_t tid1, tid2;
    pthread_create(&tid1, NULL, print1, NULL);
    pthread_create(&tid2, NULL, print2, NULL);
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    return 0;

}

3 个答案:

答案 0 :(得分:5)

多线程时不能依赖时序假设。

对这个问题的一个解释是,循环中printf s的序列执行的时间非常短,可以在给予该线程的时间量内执行。

答案 1 :(得分:3)

我对线程的理解是错误的还是我的代码不等同于问题?

都不是。您的两个线程恰好按照预期的方式安排在您的系统上。显而易见的同步有很多原因,但由于OS调度程序在系统繁忙时会暂停一个或多个线程,因此它是相当不可预测的。您可以尝试在运行示例程序的同时跨多个线程运行繁重的工作负载,并查看是否存在问题。

这就是多线程程序中出现数千个意外错误的原因,也是我们需要互斥锁,信号量,原子写入和复杂线程调试工具(如helgrind或其等价物)的原因。在处理线程共享数据时,证明没有死锁或其他同步问题是非常困难的事情。

答案 2 :(得分:1)

在访问多线程应用程序内的共享数据时,保护共享数据始终非常重要。在您的情况下,您有两个线程同时访问标准输出,因此您有一个竞争条件,其中两个线程竞争获取stdout。因此,我已经自由地使您的程序适应标准输出受互斥锁保护的情况。在使用stdout之前我们锁定互斥锁,另一个线程也会尝试锁定互斥锁,但两者都不能同时执行此操作。因此,尝试锁定的第二个线程将进入休眠状态。

正如其他人告诉你的那样,你最初编写的程序看起来很可能产生了很好的输出,或多或少的巧合,在其他系统上你无法保证这两个线程的输出可以混淆。以输出结束,如

1256734
8
5671234
8

现在您可能会认为,一旦互斥锁被解锁,另一个将自动接管,但这取决于调度程序。因此,在下面的示例中,程序可能会在输出5678之前执行几次1234.但它始终为1234或5678,并且这些输出不会混合。如果你真的想要替换两个线程,你需要更复杂的输出和查找条件变量,例如,pthread API支持那些。

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

void *print1(void *arg) {
    pthread_mutex_t* lock = arg;
    while(1) {
        /*now we do output from one thread while we lock the
         *mutex from the main function.*/
        pthread_mutex_lock(lock);
        printf("1");
        printf("2");
        printf("3");
        printf("4\n");
        fflush(stdout);
        /*now we must lock the lock, because otherwise
         *the other thread can't lock the mutex and if
         *we would try to lock the mutex again we would
         *create a deadlock, hower recursive locking is
         *possible, but beyond the scope of my anwer.*/
        pthread_mutex_unlock(lock);
    }
    return NULL;
}

void *print2(void *arg) {
    /*see the note in the frist thread*/
    pthread_mutex_t* lock = arg;
    while(1){
        pthread_mutex_lock(lock);
        printf("5");
        printf("6");
        printf("7");
        printf("8\n");
        fflush(stdout);
        pthread_mutex_unlock(lock);
    }
    return NULL;
}


int main() {
    pthread_t tid1, tid2;
    /*lets create a lock to protect our shared resources
     *in this case the standard output.
     */
    pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    /*make sure that the mutex is available inside the thread
     *by sending a pointer to the mutex as fourth argument to
     *phtread_create.*/
    pthread_create(&tid1, NULL, print1, &lock);
    pthread_create(&tid2, NULL, print2, &lock);
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    return 0;
}