我的代码中存在死锁/访问冲突,即使我已经相互排除它
Deadlock / Access violation in my code even though I've mutually excluded it
所以我试图更好地理解多线程以及死锁是如何发生的以及如何避免它们,所以我整理了一小段代码。基本上我有两个线程共享一个 int 指针。
每个线程都读取和写入 from/to 指向的值。
当一个线程写入(将值增加 1)时,我在它周围锁定一个全局互斥量。
当它读取(输出到控制台)时,我在它周围锁定了一个全局互斥体。
首先是我的代码:
// DeadLockTest.cpp
//
#include "stdafx.h"
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
void foo(int* p);
void bar(int* p);
mutex mut;
int _tmain(int argc, _TCHAR* argv[])
{
int x = 5;
int* p = &x;
thread first(bind(foo, p));
thread second(bind(bar, p));
first.join();
second.join();
cout << "threads done";
cin.ignore();
return 0;
}
void foo(int* p){
int i = 0;
while(1){
i++;
mut.lock();
*p++;
mut.unlock();
mut.lock();
cout << "foo: " << *p << endl;
mut.unlock();
}
}
void bar(int* p){
int i = 0;
while(1){
i++;
mut.lock();
*p--;
mut.unlock();
mut.lock();
cout << "bar" << *p << endl;
mut.unlock();
}
}
我最终得到一个例外:
Unhandled exception at 0x008E608F in DeadLockTest.exe: 0xC0000005: Access violation reading location 0x003B0000.
首先,为什么我会收到异常?我已经相互锁定了资源,使其无法从其他线程访问。如果我做错了什么,这会导致另一个问题。异常总是发生在 foo 中的 cout 语句上,它永远不会发生在其他任何地方(甚至在 bar 线程中也不发生)。为什么只有 foo 线程,为什么只有 cout 语句?当我 decrease/increase 引用值时呢?
其次,每个线程都应该使用自己的互斥量吗?或者可以使用全局共享的互斥锁吗?使用自己的本地互斥锁而不是全局共享互斥锁的线程之间有什么区别?
第三,如果我的线程有一个条件,所以它们不会无限运行,为什么会这样:
cout << "threads done";
在两个线程完成后调用?两个线程是异步运行的吧?
只是想为将来更好地理解这一点。
我在 linux 上试过,用 g++ -std=c++0x -pthread foo.cpp -o foo
编译,这里也有段错误。
但是,我只是尝试将 *p++
和 *p--
更改为 (*p)++
和 (*p)--
并且成功了!
问题是您递增和递减指针而不是您指向的值。试试这个:
#include <iostream>
using namespace std;
int main() {
int i = 123;
int *p;
p = &i;
cout << *p++ << endl;
cout << *p++ << endl;
cout << *p++ << endl;
cout << *p++ << endl;
cout << *p++ << endl;
}
输出为:
123
-166656888
32767
0
0
现在您会明白,访问冲突意味着您正在尝试访问您无法访问的内存。这表明您有一个指向您不希望它指向的位置的指针。
因此,当您执行 *p++
时,您是在递增指针,而不是递增值。通过首先取消引用,通过执行 (*p)
,您可以确保更改的是 值 而不是指针。
所以我试图更好地理解多线程以及死锁是如何发生的以及如何避免它们,所以我整理了一小段代码。基本上我有两个线程共享一个 int 指针。
每个线程都读取和写入 from/to 指向的值。 当一个线程写入(将值增加 1)时,我在它周围锁定一个全局互斥量。 当它读取(输出到控制台)时,我在它周围锁定了一个全局互斥体。
首先是我的代码:
// DeadLockTest.cpp
//
#include "stdafx.h"
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
void foo(int* p);
void bar(int* p);
mutex mut;
int _tmain(int argc, _TCHAR* argv[])
{
int x = 5;
int* p = &x;
thread first(bind(foo, p));
thread second(bind(bar, p));
first.join();
second.join();
cout << "threads done";
cin.ignore();
return 0;
}
void foo(int* p){
int i = 0;
while(1){
i++;
mut.lock();
*p++;
mut.unlock();
mut.lock();
cout << "foo: " << *p << endl;
mut.unlock();
}
}
void bar(int* p){
int i = 0;
while(1){
i++;
mut.lock();
*p--;
mut.unlock();
mut.lock();
cout << "bar" << *p << endl;
mut.unlock();
}
}
我最终得到一个例外:
Unhandled exception at 0x008E608F in DeadLockTest.exe: 0xC0000005: Access violation reading location 0x003B0000.
首先,为什么我会收到异常?我已经相互锁定了资源,使其无法从其他线程访问。如果我做错了什么,这会导致另一个问题。异常总是发生在 foo 中的 cout 语句上,它永远不会发生在其他任何地方(甚至在 bar 线程中也不发生)。为什么只有 foo 线程,为什么只有 cout 语句?当我 decrease/increase 引用值时呢?
其次,每个线程都应该使用自己的互斥量吗?或者可以使用全局共享的互斥锁吗?使用自己的本地互斥锁而不是全局共享互斥锁的线程之间有什么区别?
第三,如果我的线程有一个条件,所以它们不会无限运行,为什么会这样:
cout << "threads done";
在两个线程完成后调用?两个线程是异步运行的吧?
只是想为将来更好地理解这一点。
我在 linux 上试过,用 g++ -std=c++0x -pthread foo.cpp -o foo
编译,这里也有段错误。
但是,我只是尝试将 *p++
和 *p--
更改为 (*p)++
和 (*p)--
并且成功了!
问题是您递增和递减指针而不是您指向的值。试试这个:
#include <iostream>
using namespace std;
int main() {
int i = 123;
int *p;
p = &i;
cout << *p++ << endl;
cout << *p++ << endl;
cout << *p++ << endl;
cout << *p++ << endl;
cout << *p++ << endl;
}
输出为:
123
-166656888
32767
0
0
现在您会明白,访问冲突意味着您正在尝试访问您无法访问的内存。这表明您有一个指向您不希望它指向的位置的指针。
因此,当您执行 *p++
时,您是在递增指针,而不是递增值。通过首先取消引用,通过执行 (*p)
,您可以确保更改的是 值 而不是指针。