多线程队列的实现有关问题
多线程队列的实现问题?
看了一些多线程队列的例子,
用到了互斥体或者临界区, 最重要的内核对象,还有信号量
问题:为什么要使用 信号量?
一种内核对象,不够吗?
保证每次只有一个线程在访问 该队列的成员,比如头,尾指针,还有队头元素,队尾元素。
不断可以节省 内核对象,减轻代码复杂性。
事实上,很多例子,都增加了 信号量。
原因是什么?
具体的说,什么函数里使用信号量呢?
比如, get函数。
get的时候,先wait,然后,取元素,取完毕,就释放元素,释放的过程中,肯定加锁(用上文提到的临界区或者互斥体实现)。
为什么,要wait?
伪代码如下:
template <class T>
void CMyQueue< T >::Get(T &buf)
{
pOccupiedSema.Wait(); //内部waitforsingleobject
int ntest = 0;
MyGet(buf); ///内部lock
}
template <class T>
void CMyQueue< T >::Put(T &newData)
{
if( IsOccupied() )
return ;
MyPut(newData); //lock
pOccupiedSema.Post(); //内部releaseseampore
}
------解决方案--------------------
用处当然是有的。
伪代码简单举个例,有三个线程,共用一个内核对象:
先用事件HEvent hEvent,自动重置事件
线程1
{
SetEvent(hEvent);
}
线程2
{
WaitForSinleObject(hEvent,0xFFFFFFFF);
{
//执行一些较复杂的代码段2
}
SetEvent(hEvent);
}
线程3
{
WaitForSinleObject(hEvent,0xFFFFFFFF);
{
//执行一些较复杂的代码段3
}
SetEvent(hEvent);
}
当在线程1中SetEvent(hEvent)后,线程2的代码段2和线程3中的代码段3只能有其中一个先执行,执行完了后另一个才能执行,即所谓的同步。
而如果改用信号量HANDLE hSemaphore = ::CreateSemaphore(NULL, 0, 2, NULL);
线程1
{
ReleaseSemaphore(hSemaphore,2,0); //增加2个信号量
}
线程2
{
WaitForSinleObject(hSemaphore,0xFFFFFFFF); //使用1个信号量
{
//执行一些较复杂的代码段2
}
}
线程3
{
WaitForSinleObject(hSemaphore,0xFFFFFFFF);//使用1个信号量
{
//执行一些较复杂的代码段3
}
}
这样的话,代码段2和代码段3是异步执行的。
以上是2个线程等待另1线程的信号的情况,再说个1个线程等待2个线程的信号的情况。
线程1
{
ReleaseSemaphore(hSemaphore,1,0); //增加1个信号量
}
线程2
{
ReleaseSemaphore(hSemaphore,1,0); //增加1个信号量
}
线程3
{
while(true)
{
WaitForSinleObject(hSemaphore,0xFFFFFFFF);//使用1个信号量
{
//执行一些较复杂的代码段3
}
}
}
代码段3能执行2次。如果改用事件,在线程1和线程2的SetEvent趋于同时的时候,代码段3可能就只执行1次了。
看了一些多线程队列的例子,
用到了互斥体或者临界区, 最重要的内核对象,还有信号量
问题:为什么要使用 信号量?
一种内核对象,不够吗?
保证每次只有一个线程在访问 该队列的成员,比如头,尾指针,还有队头元素,队尾元素。
不断可以节省 内核对象,减轻代码复杂性。
事实上,很多例子,都增加了 信号量。
原因是什么?
具体的说,什么函数里使用信号量呢?
比如, get函数。
get的时候,先wait,然后,取元素,取完毕,就释放元素,释放的过程中,肯定加锁(用上文提到的临界区或者互斥体实现)。
为什么,要wait?
伪代码如下:
template <class T>
void CMyQueue< T >::Get(T &buf)
{
pOccupiedSema.Wait(); //内部waitforsingleobject
int ntest = 0;
MyGet(buf); ///内部lock
}
template <class T>
void CMyQueue< T >::Put(T &newData)
{
if( IsOccupied() )
return ;
MyPut(newData); //lock
pOccupiedSema.Post(); //内部releaseseampore
}
------解决方案--------------------
用处当然是有的。
伪代码简单举个例,有三个线程,共用一个内核对象:
先用事件HEvent hEvent,自动重置事件
线程1
{
SetEvent(hEvent);
}
线程2
{
WaitForSinleObject(hEvent,0xFFFFFFFF);
{
//执行一些较复杂的代码段2
}
SetEvent(hEvent);
}
线程3
{
WaitForSinleObject(hEvent,0xFFFFFFFF);
{
//执行一些较复杂的代码段3
}
SetEvent(hEvent);
}
当在线程1中SetEvent(hEvent)后,线程2的代码段2和线程3中的代码段3只能有其中一个先执行,执行完了后另一个才能执行,即所谓的同步。
而如果改用信号量HANDLE hSemaphore = ::CreateSemaphore(NULL, 0, 2, NULL);
线程1
{
ReleaseSemaphore(hSemaphore,2,0); //增加2个信号量
}
线程2
{
WaitForSinleObject(hSemaphore,0xFFFFFFFF); //使用1个信号量
{
//执行一些较复杂的代码段2
}
}
线程3
{
WaitForSinleObject(hSemaphore,0xFFFFFFFF);//使用1个信号量
{
//执行一些较复杂的代码段3
}
}
这样的话,代码段2和代码段3是异步执行的。
以上是2个线程等待另1线程的信号的情况,再说个1个线程等待2个线程的信号的情况。
线程1
{
ReleaseSemaphore(hSemaphore,1,0); //增加1个信号量
}
线程2
{
ReleaseSemaphore(hSemaphore,1,0); //增加1个信号量
}
线程3
{
while(true)
{
WaitForSinleObject(hSemaphore,0xFFFFFFFF);//使用1个信号量
{
//执行一些较复杂的代码段3
}
}
}
代码段3能执行2次。如果改用事件,在线程1和线程2的SetEvent趋于同时的时候,代码段3可能就只执行1次了。