线程同步使用锁出现的奇怪问题
hdqqq 2005-12-16 11:30:38 mfc线程同步的时候,可以用CMutex或CCriticalSection,并用CSingleLock来进行锁定
象下面的代码
//CMutex gbmutex;
CCriticalSection gbmutex;
UINT thread1(LPVOID lparam)
{
int i;
CSingleLock lock(&gbmutex);
lock.Lock();
for (i = 0; i < 4; i++) {
cout << "1 thread output " << i << endl;
Sleep(1000);
}
lock.Unlock();
return 0;
}
UINT thread2(LPVOID lparam)
{
int i;
CSingleLock lock(&gbmutex);
lock.Lock();
for ( i = 0; i < 4; i++) {
cout << "2 thread output" << endl;
}
lock.Unlock();
if (lparam) {
CEvent* pevent = (CEvent*)lparam;
pevent->SetEvent();
}
return 0;
}
void thread_test()
{
CEvent le;
AfxBeginThread(thread1,NULL);
Sleep(1000);
le.ResetEvent();
AfxBeginThread(thread2,(void*)&le);
CSingleLock lock(&le);
lock.Lock();
}
thread_test中刻意让thread1先启动,并等了1秒
上面的代码在标准的控制台输出的时候如下:
1 thread output 0
1 thread output 1
1 thread output 2
1 thread output 3
2 thread output
2 thread output
2 thread output
2 thread output
但是,觉得这样使用数据同步的方式比较麻烦,所以,自己写了一个数据同步的适配器
代码如下:
template <class _data, class _mutex, class _lock>
class Mutex_Access_Adapter : public _data
{
private:
_mutex m_Mutex;
public:
BOOL Lock() {
_lock lock(&m_Mutex);
return lock.Lock();
}
BOOL Unlock() {
_lock lock(&m_Mutex);
return lock.Unlock();
}
public:
Mutex_Access_Adapter() {}
~Mutex_Access_Adapter() {}
};
上面的适配器自带了锁对象和同步的函数,所以,对于需要在线程之间需要同步操作的类
对象,可以这样使用.
struct __data_struct
{
};
typedef Mutex_Access_Adapter<__data_struct, CMutex, CSingleLock> threadsafe_data;
上面的threadsafe_data就自己带了同步的方式了.
然后,我写了下面的测试代码
threadsafe_data gbdata;
UINT thread3(LPVOID lparam)
{
int i;
gbdata.Lock();
for (i = 0; i < 4; i++) {
cout << "3 thread output " << i << endl;
Sleep(1000);
}
gbdata.Unlock();
return 0;
}
UINT thread4(LPVOID lparam)
{
int i;
gbdata.Lock();
for ( i = 0; i < 4; i++) {
cout << "4 thread output" << endl;
}
gbdata.Unlock();
if (lparam) {
CEvent* pevent = (CEvent*)lparam;
pevent->SetEvent();
}
return 0;
}
void other_thread_test()
{
CEvent le;
AfxBeginThread(thread3,NULL);
Sleep(1000);
le.ResetEvent();
AfxBeginThread(thread4,(void*)&le);
CSingleLock lock(&le);
lock.Lock();
}
上面的代码中thread3和thread4和thread1和thread2差不多,只是使用
了适配器修饰后的类的锁函数,但是输出结果如下:
3 thread output 0
3 thread out4 thread output
4 thread outpuput 1
t
4 thread output
4 thread output
这个没有象预期的那样,居然没有锁住.
奇怪之余,我看了mfc中CSingleLock中的实现代码,自己写了一个类似接口的
锁类.
//自定义的线程锁
template <typename _locktype>
class thread_lock
{
private:
_locktype* m_pLocktype;
public:
BOOL Lock() {
return m_pLocktype->Lock();
}
BOOL Unlock() {
return m_pLocktype->Unlock();
}
public:
thread_lock(_locktype* value) : m_pLocktype(value) {}
~thread_lock(){}
};
然后把上面的那个typedef 改写为
typedef Mutex_Access_Adapter<__data_struct, CMutex, thread_lock<CMutex> > threadsafe_data;
然后再运行上面的代码结果如下:
3 thread output 0
3 thread output 1
3 thread output 2
3 thread output 3
4 thread output
4 thread output
4 thread output
4 thread output
居然就锁住了,肯定是CSingleLock的区别,我自己写的锁没有CSingleLock那么复杂,但是我看CSingleLock的代码,它的构造函数
也很简单,很奇怪.
现在的问题是:
1. 如果一个对象的指针作为参数传入某个线程,如果线程通过这个指针调用对象的成员函数,那个成员函数中的 局部变量
是不是在线程的运行栈中构造? 我认为是的,不知道对不对.
2.为什么相同的使用方式,CSingleLock没锁住,自写的却锁住了,是否Mutex句柄有属于那个线程的区别,CSingleLock是否
可以递归锁定,说不清楚原因究竟在那里,所以希望大家能讨论一下.
上面的代码是在xp vc6 sp6 下测试的,可以建立一个支持mfc 的控制台程序,复制上面的代码测试,vc7下面我没有测试,欢迎大家来参与讨论.