2016-04-21 110 views
0

考虑下一个代码块 -怪异pthread_mutex_t行为

#include <iostream> 

using namespace std; 

int sharedIndex = 10; 
pthread_mutex_t mutex; 

void* foo(void* arg) 
{ 
    while(sharedIndex >= 0) 
    { 
     pthread_mutex_lock(&mutex); 

     cout << sharedIndex << endl; 
     sharedIndex--; 

     pthread_mutex_unlock(&mutex); 
    } 

    return NULL; 
} 

int main() { 

    pthread_t p1; 
    pthread_t p2; 
    pthread_t p3; 

    pthread_create(&p1, NULL, foo, NULL); 
    pthread_create(&p2, NULL, foo, NULL); 
    pthread_create(&p3, NULL, foo, NULL); 

    pthread_join(p1, NULL); 
    pthread_join(p2, NULL); 
    pthread_join(p3, NULL); 

    return 0; 
} 

我只是创建了三个pthreads并给他们所有相同功能foo,在此希望每个线程,在其反过来,将打印和递减sharedIndex

但这是输出 -

10 
9 
8 
7 
6 
5 
4 
3 
2 
1 
0 
-1 
-2 
  • 我不明白为什么当sharedIndex 达到0
  • sharedIndexmutex保护的进程不会停止。在它变成0之后它是如何访问的?线程是不是应该直接跳至return NULL;

编辑

此外,似乎只有第一个线程递减sharedIndex。 为什么不是每个线程都在递减共享资源? 这里有一个修正后的输出 -

Current thread: 140594495477504 
10 
Current thread: 140594495477504 
9 
Current thread: 140594495477504 
8 
Current thread: 140594495477504 
7 
Current thread: 140594495477504 
6 
Current thread: 140594495477504 
5 
Current thread: 140594495477504 
4 
Current thread: 140594495477504 
3 
Current thread: 140594495477504 
2 
Current thread: 140594495477504 
1 
Current thread: 140594495477504 
0 
Current thread: 140594495477504 
Current thread: 140594478692096 
Current thread: 140594487084800 

我希望所有的线程将递减共享源代码 - 这意味着,每一个开关CONTEX,不同的线程将访问资源,做它的事。

+2

尝试在程序的最后创建线程和“pthread_mutex_destroy”之前调用'pthread_mutex_init' – mausik

+0

为什么不尝试在代码中修复未定义的行为并查看它是否有帮助? –

+0

我确实改变了代码,它工作。但仍然 - 只有一个线程递减资源 –

回答

4

Th程序的行为是未定义的。

您还没有初始化互斥锁。你需要或者调用pthread_mutex_init或静态初始化:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 

你读这个变量的临界区外:

while(sharedIndex >= 0) 

这意味着,而另一个线程正在更新它,你可以读取垃圾值。在锁定互斥锁并拥有对其的独占访问权限之前,您不应该读取共享变量。

编辑:

似乎只有第一个线程递减sharedIndex

这是因为不确定的行为。解决上述问题,你应该看到其他线程运行。

使用您当前的代码,编译器可以假定sharedIndex从不会被其他线程更新,所以它不会重新读取它,但只是让第一个线程运行十次,然后其他两个线程每个运行一次。

意思是,每一个交换机,一个不同的线程都会访问资源并执行它的操作。

无法保证pthread mutexes的公平行为。如果你想保证每个线程轮流运行的循环行为,那么你就需要自己强加这个行为。通过让另一个共享变量(也许是一个条件变量)来说明它将运行哪个线程,并阻止其他线程,直到轮到他们。

3

线程将挂在pthread_mutex_lock(&mutex);等待获取锁定。一旦线程递减到0并释放锁,下一个等待锁的线程将执行它的业务(使值为-1),并且对于下一个线程(使值为-2)相同。

你需要改变你的逻辑检查值和锁定互斥锁。

3
int sharedIndex = 10; 
pthread_mutex_t mutex; 

void* foo(void* arg) 
{ 
    while(sharedIndex >= 0) 
    { 
     pthread_mutex_lock(&mutex); 

     cout << sharedIndex << endl; 
     sharedIndex--; 

     pthread_mutex_unlock(&mutex); 
    } 

    return NULL; 
} 

根据这个代码sharedIndex是所有线程共享资源

因此,每个对它的访问(包括读写)都应该被互斥包装。 否则,假设所有线程同时采样sharedIndex并且其值为1

然后,所有线程输入while循环,每个线程将sharedIndex减1,最后将其导向-2

编辑

可能修复(如可能的选项之一):

bool is_positive; 
do 
{ 
    pthread_mutex_lock(&mutex); 

    is_positive = (sharedIndex >= 0); 
    if (is_positive) 
    { 
     cout << sharedIndex << endl; 
     sharedIndex--; 
    } 

    pthread_mutex_unlock(&mutex); 
}while(is_positive); 

EDIT2

注意,您必须初始化互斥锁:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 
+1

直到最后一段,它赢得了赞赏。'volatile'在这里没有用,编译器永远不会移动互斥锁之外的变量访问(除非编译器完全被破坏),所以使用寄存器无关紧要,只要结果结束内存在关键部分的末尾。 –

+0

谢谢你,你可能的解决办法做到了。 –

+0

@JonathanWakely编译器可能(可能)在函数的开始处将'sharedIndex'移动到'eax',并在'return'之前将其存回'sharedIndex'。也许它不会这样做,但我不知道是否可以应用任何“法律”,这将阻止编译器这样做。如果编译器知道'pthread_mutex_lock'和'pthread_mutex_unlock'的实现,可能会得出结论:没有任何影响'sharedIndex'和'sharedIndex'的结果不影响这些函数。所以这样的优化是可能的。 –