何时使用互斥锁

时间:2012-01-06 12:21:50

标签: c++ mutex

这就是:有一个float数组float bucket[5]和2个线程,比如thread1和thread2。

Thread1负责对bucket进行调整,为bucket中的每个元素分配一个随机数。当桶装满时,thread2将访问bucket并读取其元素。

以下是我的工作方式:

float bucket[5];
pthread_mutex_t mu = PTHREAD_MUTEX_INITIALIZER;
pthread_t thread1, thread2;

void* thread_1_proc(void*);  //thread1's startup routine, tank up the bucket 
void* thread_2_proc(void*);  //thread2's startup routine, read the bucket 

int main()
{
    pthread_create(&thread1, NULL, thread_1_proc, NULL);
    pthread_create(&thread2, NULL, thread_2_proc, NULL);
    pthread_join(thread1);
    pthread_join(thread2);
}

以下是我对thread_x_proc的实现:

void* thread_1_proc(void*)
{
    while(1) { //make it work forever
        pthread_mutex_lock(&mu);  //lock the mutex, right?

        cout << "tanking\n";
        for(int i=0; i<5; i++)
            bucket[i] = rand();  //actually, rand() returns int, doesn't matter

        pthread_mutex_unlock(&mu); //bucket tanked, unlock the mutex, right?

        //sleep(1); /* this line is commented */
    }
}

void* thread_2_proc(void*)
{
    while(1) { 
        pthread_mutex_lock(&mu);

        cout << "reading\n";
        for(int i=0; i<5; i++)
            cout << bucket[i] << " ";  //read each element in the bucket

        pthread_mutex_unlock(&mu); //reading done, unlock the mutex, right?

        //sleep(1); /* this line is commented */
    }
}

问题

我的实施是否合适?但是输出并不像我预期的那样。

...
reading
5.09434e+08 6.58441e+08 1.2288e+08 8.16198e+07 4.66482e+07 7.08736e+08 1.33455e+09 
reading
5.09434e+08 6.58441e+08 1.2288e+08 8.16198e+07 4.66482e+07 7.08736e+08 1.33455e+09 
reading
5.09434e+08 6.58441e+08 1.2288e+08 8.16198e+07 4.66482e+07 7.08736e+08 1.33455e+09 
reading
tanking
tanking
tanking
tanking
...

如果我取消注释每个thread_x_proc函数中的sleep(1);,则输出正确,tankingreading互相跟随,如下所示:

...
tanking
reading
1.80429e+09 8.46931e+08 1.68169e+09 1.71464e+09 1.95775e+09 4.24238e+08 7.19885e+08 
tanking
reading
1.64976e+09 5.96517e+08 1.18964e+09 1.0252e+09 1.35049e+09 7.83369e+08 1.10252e+09 
tanking
reading
2.0449e+09 1.96751e+09 1.36518e+09 1.54038e+09 3.04089e+08 1.30346e+09 3.50052e+07
...

为什么呢?使用sleep()时,我应该使用mutex吗?

6 个答案:

答案 0 :(得分:4)

您的代码技术上正确,但它没有多大意义,并且它没有做您所假设的。

你的代码做什么,它以原子方式更新数据部分,并以原子方式从该部分读取。但是,您不知道这种情况发生的顺序,也不知道数据在读取之前写入的频率(或者根本不是!)。

您可能想要的每次在一个线程中生成一个数字序列,并且每次在另一个线程中只读取一个新序列。为此,您将使用必须使用额外的信号量或更好的单生产者单消费者队列。

一般来说,“我什么时候应该使用互斥锁”的答案是“从不,如果你可以帮助它”。线程应该发送消息,而不是共享状态。这使得互斥锁大部分时间都不必要,并提供并行性(这是首先使用线程的主要动机)。

互斥锁使你的线程运行锁步,所以你也可以在一个线程中运行。

答案 1 :(得分:2)

线程没有隐含的顺序可以运行。这意味着您不会指望任何订单。更有可能让线程一次又一次地运行而不让对方运行。这是特定于实现的,应该假设是随机的。

您提交的案例非常适用于添加了每个元素的“发布”的信号量。

但如果它总是如此:

  • 写5个元素
  • 阅读5个元素

你应该有两个互斥:

  • 阻止生产者直到消费者完成
  • 的那个
  • 在生产者完成之前阻止消费者的

所以代码看起来应该是这样的:

Producer:
    while(true){
        lock( &write_mutex )
        [insert data]
        unlock( &read_mutex )
    }

Consumer:
    while(true){
        lock( &read_mutex )
        [insert data]
        unlock( &write_mutex )
    }

最初write_mutex应解锁并read_mutex锁定。

正如我所说,你的代码似乎是信号量或条件变量的更好的例子。 互斥体不适用于这种情况(这并不意味着你不能使用它们,它只是意味着有更多方便的工具来解决这个问题)。

答案 2 :(得分:2)

您无权假设仅仅因为您希望线程以特定顺序运行,实现将找出您想要的内容并按实际顺序运行它们。

为什么不应该在thread1之前运行thread2?为什么每个线程都不应该在另一个线程有机会运行到获取互斥锁的行之前多次完成它的循环?

如果您希望执行以可预测的方式在两个线程之间切换,那么您需要使用信号量,条件变量或其他机制在两个线程之间进行消息传递。 sleep似乎会在这种情况下产生您想要的顺序,但即使在睡眠状态下,您还没有做足以保证它们会交替进行。我不知道为什么sleep会对哪个线程首先运行产生影响 - 在多次运行中是否一致?

答案 3 :(得分:1)

如果你有两个应该按顺序执行的功能,即F1应该在F2启动之前完成,那么你不应该使用两个线程。 F1返回后,在与F1相同的线程上运行F2。

没有线程,你也不需要互斥锁。

答案 4 :(得分:0)

这不是问题。

睡眠只允许'其他'线程访问互斥锁(偶然,它正在等待锁定,所以它可能会有互斥锁),你无法确定第一个线程是否会被重置 - 锁定互斥锁,让其他线程访问它。

Mutex用于保护数据,因此两个线程不会: a)同时写 b)当另一个人正在阅读时,一个人正在写作

不是让线程以某种顺序工作(如果你想要那个功能,抛弃线程方法或者使用一个标志来告诉'tank'已经满了)。

答案 5 :(得分:0)

到目前为止,从其他答案中可以清楚地看出原始代码中的错误是什么。所以,让我们尝试改进它:

/* A flag that indicates whose turn it is. */
char tanked = 0;

void* thread_1_proc(void*)
{
    while(1) { //make it work forever
        pthread_mutex_lock(&mu);  //lock the mutex
        if(!tanked) { // is it my turn?
            cout << "tanking\n";
            for(int i=0; i<5; i++)
                bucket[i] = rand();  //actually, rand() returns int, doesn't matter
            tanked = 1;
        }
        pthread_mutex_unlock(&mu); // unlock the mutex
    }
}

void* thread_2_proc(void*)
{
    while(1) {
        pthread_mutex_lock(&mu);
        if(tanked) { // is it my turn?
            cout << "reading\n";
            for(int i=0; i<5; i++)
                cout << bucket[i] << " ";  //read each element in the bucket
            tanked = 0;
        }
        pthread_mutex_unlock(&mu); // unlock the mutex
    }
}

上面的代码应该按预期工作。但是,正如其他人所指出的那样,使用以下两个选项之一可以更好地完成结果:

  • 按顺序。由于生产者和消费者必须交替,因此您不需要两个线程。坦克然后读取的一个循环就足够了。此解决方案还可以避免上述代码中发生的繁忙等待。
  • 使用信号量。如果生产者能够连续多次运行,在存储桶中累积元素(不是原始代码中的情况),这将是解决方案。 http://en.wikipedia.org/wiki/Producer-consumer_problem#Using_semaphores