关于多线程操作同一个队列时如何互斥

gungod 2011-06-29 10:15:55
有一个接收线程,一个队列,N个处理线程;
a.接收线程负责不断接收数据包写入队列;
b.处理线程从队列中读取数据包后,删除该数据包,再处理数据包;

关于队列的读写操作,我担心N个处理线程同时读取了队列的同一个数据包,做删除时,删除了未读取的数据包;
想加个一个保护,线程依次访问,是否应该用互斥,该如何写?
...全文
508 点赞 收藏 20
写回复
20 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
gungod 2011-07-04
保护队列数据的互斥我应该是成功了,
效率问题跟这个无关,结贴了
回复
laowang2 2011-07-01
学习学习。
回复
Waiting4you 2011-07-01
[Quote=引用 12 楼 waiting4you 的回复:]
关于效率,有什么办法提高?
[/Quote]
stl的队列不是对多线程优化的,你可以到网上找找专门的多线程安全队列(比如TBB的无锁队列)。如果要求不是太高,你上面的代码也可以做一些简单的优化:
g_Lock->Acquire();
if(g_TcpDataQueue1.size()>0)
{
_AnalyseString = g_TcpDataQueue1.front();
g_TcpDataQueue1.pop();
}
g_Lock->Release();
改成
if(!g_TcpDataQueue1.empty())
{
g_Lock->Acquire();
if(!g_TcpDataQueue1.empty())
{
_AnalyseString = g_TcpDataQueue1.front();
g_TcpDataQueue1.pop();
}
g_Lock->Release();
}
这样可以避免空队列时多个线程不断地Acquire/Release
回复
tst1255 2011-07-01
如果当初放入队列的时候是用new 或malloc 申请的内存,放入队列应该不会有问题,如果是把一个AnsiString使用c_str()方法转换成char *放入队列,那就可能有问题。

楼主的接收的数据包里都是字符信息码? 如果有二进制数据用string处理不是好方法。

通常情况下数据包需要自己定义协议,如命令类型、数据类型、数据长度之类的信息,纯字符的通信情况较少吧。

效率问题应该是根据通讯速率,每个数据包处理的时间,线程的多少去优化,不是线程越多越好。CPU的核就那几个,线程多了调度放入开销就大。如果每个包处理的时间很短,多线程还不如单线程效率高。

回复
CppFile 2011-07-01
尽量少在acquire和release之间操作,让资源尽快释放给别的线程用
回复
gungod 2011-07-01
[Quote=引用 12 楼 waiting4you 的回复:]
queue<char*> g_TcpDataQueue1,如果里面存放的是字符串的话,很有可能有问题,建议如string或String来代替char*
只要做到对g_TcpDataQueue1的存取全部位于g_Lock->Acquire()和g_Lock->Release();之间,那就不会有问题,就是效率可能会有点慢。
[/Quote]

的确是char*的问题,我没有new,呵呵,
关于效率,有什么办法提高?
回复
gungod 2011-07-01
[Quote=引用 18 楼 microheart 的回复:]
可以用临界区变量TCriticalSection
定义全局变量
TCriticalSection *crtSection = new TCriticalSection();
线程中使用时代码如下示例:
crtSection->Enter();
__try
{
//队列操作;
}
__finally
{
crtSection->Leave();
}
最后程序结束时把这个……
[/Quote]

之所以用队列,就是因为它是FIFO,我只对队列首尾操作,应该比LIST快吧。
回复
microheart 2011-07-01
可以用临界区变量TCriticalSection
定义全局变量
TCriticalSection *crtSection = new TCriticalSection();
线程中使用时代码如下示例:
crtSection->Enter();
__try
{
//队列操作;
}
__finally
{
crtSection->Leave();
}
最后程序结束时把这个临界区变量咔嚓了
delete crtSection;

当然也可以使用TThreadList来进行
使用方法为使用时用LockList()方法;
使用完成后使用UnlockList()方法;

另外就是如果使用TList类,删除数据从尾部操作效率会高得很多
如:
List->Delete(List->Count-1);
List->Delete(0);
以上两种操作,第一种效率会好很多,你懂得!
回复
tst1255 2011-06-30
[Quote=引用 6 楼 gungod 的回复:]
引用 5 楼 ice 的回复:
  class MyThread : public TThread
  {
   ...
  private:
  TCriticalSection pLockX;
  int x;
  float y;
  ...
  };
  void __fastcall MyThread::Execute()
……


我现在用的就是临界区,但是……
[/Quote]

临界区变量定义在线程内吗?在哪里创建呢,你该不会是每个线程里都创建了一个临界区吧?线程各用各的临界区就起不到同步作用了。

定义全局临界区变量,访问共享资源时,进入该临界区,结束访问退出临界区。 线程间要使用同一个临界区才会有效果。
回复
CppFile 2011-06-30
用了临界区,不应该冲突的,线程在碰到临界区资源的时候,会等待的
回复
Waiting4you 2011-06-30
queue<char*> g_TcpDataQueue1,如果里面存放的是字符串的话,很有可能有问题,建议如string或String来代替char*
只要做到对g_TcpDataQueue1的存取全部位于g_Lock->Acquire()和g_Lock->Release();之间,那就不会有问题,就是效率可能会有点慢。
回复
gungod 2011-06-30
[Quote=引用 5 楼 ice 的回复:]
  class MyThread : public TThread
  {
   ...
  private:
  TCriticalSection pLockX;
  int x;
  float y;
  ...
  };
  void __fastcall MyThread::Execute()
……
[/Quote]

我现在用的就是临界区,但是还是有冲突,开始我还以为临界区是只是保护资源,线程不等待的。
估计还有冲突的。
回复
tst1255 2011-06-30
读、写线程都要使用临界区

队列的指针是指向一个个的数据包吧,它们是接收线程加进去的,正好都是一个个独立的数据包吗?

“队列处理老是少了数据”是指队列里的数据包数少了,还是一个数据包里的数据少了。是不是接收线程没有分包好,把几个包的数据当成一个包插入到队列里了?

处理线程得到数据包后检查一下数据包的数据,有没有把两个包放在一起的可能。

回复
gungod 2011-06-30
g_Lock是临界区对象,TcpDataQueue1是queue,
它们都是全局变量,
extern queue<char*> g_TcpDataQueue1; //TCP接收数据包队列
extern TCriticalSection *g_Lock; //临界区

g_Lock = new TCriticalSection ();

g_TcpDataQueue1倒是没有设置最大数目,难道是这个问题?
回复
gungod 2011-06-30
我的处理线程,负责取出队列头部的数据,并把队头的数据删除;
g_Lock是临界区对象,TcpDataQueue1是queue,
当我发现我读取这个队列处理老是少了数据,是不是下面的写法不对?

g_Lock->Acquire();
if(g_TcpDataQueue1.size()>0)
{
_AnalyseString = g_TcpDataQueue1.front();
g_TcpDataQueue1.pop();
}
g_Lock->Release();



回复
CppFile 2011-06-29
用临界区API

void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
void LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
void DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection);


就这么几个函数

每个线程要操作队列的时候,enter操作完毕后leave

回复
开始领悟 2011-06-29
  class MyThread : public TThread
  {
   ...
  private:
  TCriticalSection pLockX;
  int x;
  float y;
  ...
  };
  void __fastcall MyThread::Execute()
  {
  ...
  pLockX-> Acquire();//Here pLockX is a Global CriticalSection variable.
  x++;
  y=sin(x);
  pLockX-> Release();
  ...
  }
回复
tst1255 2011-06-29
BCB有一个 TThreadList对象,它是线程安全的,不需要再使用其它同步方法了。
回复
sczyq 2011-06-29
用 TThreadList 来管理

TMyStruct * AStruct = NULL;
try
{
TList * AList = FThreadList->LockList();
if (AList->Count)
{
AStruct = (TMyStruct *)AList->Items[0];
AList->Delete(0);
}
}
__finally
{
FThreadList->UnlockList();
}


if (AStruct)
{
// 处理这个 AStruct
delete AStruct;
}
回复
ccrun.com 2011-06-29
一般多线程中保持同步用的几种方式:
互斥,信号灯,临界,事件
回复
相关推荐
发帖
网络及通讯开发
创建于2007-08-02

1300

社区成员

C++ Builder 网络及通讯开发
申请成为版主
帖子事件
创建了帖子
2011-06-29 10:15
社区公告
暂无公告