几种线程间通信方式的比较?求助

orasy 2011-06-24 12:59:45
现在在Windows下编程,是实时处理海量数据的要求,这时性能有较高的要求。现在的问题是这样的:

一个线程A产生数据,累积到一定数量后交给线程B去后续处理(例如写盘什么的),其实是满简单的一个需求了,我就开辟了2个buffer,我的想法是先让A获得第一个buffer的控制权,然后线程B再去等第一buffer控制权,当第一个buffer写满后,线程A释放第一个buffer的控制权,再去获得第二的buffer的控制权。线程B获得第一个buffer的控制权后处理数据,然后释放第一个buffer的控制权,再去等第二个buffer的控制权。以此交替。

我可以采用EnterCriticalSection,LeaveCriticalSection来解决这个同步,但是问题是,当A线程对buffer操作时,如果B也在等同一个buffer的控制权,这时候B是不是一直在内核模式和用户模式下切换,耗费资源非常大?有没有其他合理的办法。

因为程序中很多对这样的线程,我的目标要求是:
当A线程操作完成后,通知B,B在获得控制权前应该是"休眠"状态,不太耗费资源。对windows下的多线程了解不多,不知道那种同步互斥方式做合适我的应用。谢谢!!!!
...全文
1569 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
safeqq2 2011-07-01
  • 打赏
  • 举报
回复
数据不是超大就不需要建立太大的缓冲机制 第一内存消耗 第二资源消耗 第三管理复杂 你的想法几乎可以满足大部分的需求了 想法很不错了
orasy 2011-06-24
  • 打赏
  • 举报
回复
你说的那个项目和我说的应用要求比较类似,我再研究一下那个环形buffer的后,载请教。谢谢!!
[Quote=引用 13 楼 kyotrue 的回复:]

在临界区上等待、WaitForSingleObject阻塞都不会消耗CPU资源的。

不要2个缓冲切换,用循环缓冲。
如果想简单,就用一个临界区就是效率最高的;如果想效率更高,就用无锁编程,其实循环缓冲你这种情况也没什么加锁的必要。

我才做过相关项目,一个生产者线程不停往缓冲区写,一个线程当缓冲区满一定程度就往磁盘上写。可以明确的告诉你,多个缓冲切换非常的麻烦、非常的容易出错、非常的……
[/Quote]
kyotrue 2011-06-24
  • 打赏
  • 举报
回复
在临界区上等待、WaitForSingleObject阻塞都不会消耗CPU资源的。

不要2个缓冲切换,用循环缓冲。
如果想简单,就用一个临界区就是效率最高的;如果想效率更高,就用无锁编程,其实循环缓冲你这种情况也没什么加锁的必要。

我才做过相关项目,一个生产者线程不停往缓冲区写,一个线程当缓冲区满一定程度就往磁盘上写。可以明确的告诉你,多个缓冲切换非常的麻烦、非常的容易出错、非常的不易排查问题,解决好同步问题会花费你非常多的时间。
orasy 2011-06-24
  • 打赏
  • 举报
回复
继续讨论。如果是这样的话,是否可以这样实现效率会好一些呢?

创建一个Event,B(消费者)等待此event(等不到立即退出),如果等不到就sleep一段时间,再去等,直到等到为止,如果等到了,就尝试进入CS。生产者在将buffer填满后,设置event,并离开CS。这样的实现会比B一直等待进入CS更有效率吗?当然这样方式降低了实时性
kyotrue 2011-06-24
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 orasy 的回复:]
再说明下,在我说的应用中,A线程生成者预期是较耗时的(累积数据),而B消费者预期是较快的,那是否也就说明,B线程因为多次尝试进入临界区不成功,最后也是转为内核态了呢?
[/Quote]

是的
alexmayer 2011-06-24
  • 打赏
  • 举报
回复
帮顶,等明白人。
orasy 2011-06-24
  • 打赏
  • 举报
回复
再说明下,在我说的应用中,A线程生成者预期是较耗时的(累积数据),而B消费者预期是较快的,那是否也就说明,B线程因为多次尝试进入临界区不成功,最后也是转为内核态了呢?
kyotrue 2011-06-24
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 orasy 的回复:]
我以前倒是看到过用volatile int 这个修饰符来定义一个环形buffer的读指针和写指针便宜量(据说读取这样的一个值是CPU的原子操作)。
在你贴出的代码中,如果保证一个线程读fifo->out 和另外个线程修改这个值得矛盾呢?


引用 5 楼 kyotrue 的回复:

你这种需求,采用一个循环缓冲就行了,只有一个线程写、一个线程读,可以采用无锁编程。

http://……
[/Quote]

即使写线程用来计算的读指针的值已经“过期”了,导致的结果也不过是写线程计算出的可用buffer空间比当前实际的小而已,但是绝对不会产生冲突的;反之读线程也一样。



另外,临界区实现分两步,首先是利用类似Interlocked的原子操作实现在用户代码空间进入临界区,如果多次尝试不成功,就进入内核态,类似于WaitForSingleObject了
chenjiawei007 2011-06-24
  • 打赏
  • 举报
回复
临界区 等待共有对象时,会使得其他等待的线程进行挂起操作,只是这个挂起与唤醒调度是底层去完成,具体怎么调度很难讲吧,只是推荐不宜等待过久。

做同步还有Interlocked系列的枷锁,是算原子操作了,但是用起来麻烦,不如临界区方便。
只要尽量减少临界区中所等待的时间与加锁次数,就能提高同步效率。
orasy 2011-06-24
  • 打赏
  • 举报
回复
我以前倒是看到过用volatile int 这个修饰符来定义一个环形buffer的读指针和写指针便宜量(据说读取这样的一个值是CPU的原子操作)。
在你贴出的代码中,如果保证一个线程读fifo->out 和另外个线程修改这个值得矛盾呢?

[Quote=引用 5 楼 kyotrue 的回复:]

你这种需求,采用一个循环缓冲就行了,只有一个线程写、一个线程读,可以采用无锁编程。

http://www.ibm.com/developerworks/cn/linux/l-cn-lockfree/index.html
最后部分就是

C/C++ code

/*
* __kfifo_put - puts some data into the ……
[/Quote]
kyotrue 2011-06-24
  • 打赏
  • 举报
回复
你这种需求,采用一个循环缓冲就行了,只有一个线程写、一个线程读,可以采用无锁编程。

http://www.ibm.com/developerworks/cn/linux/l-cn-lockfree/index.html
最后部分就是

				
/*
* __kfifo_put - puts some data into the FIFO, no locking version
* Note that with only one concurrent reader and one concurrent
* writer, you don't need extra locking to use these functions.
*/
unsigned int __kfifo_put(struct kfifo *fifo,
unsigned char *buffer, unsigned int len)
{
unsigned int l;
len = min(len, fifo->size - fifo->in + fifo->out);
/* first put the data starting from fifo->in to buffer end */
l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));
memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l);
/* then put the rest (if any) at the beginning of the buffer */
memcpy(fifo->buffer, buffer + l, len - l);
fifo->in += len;
return len;
}

/*
* __kfifo_get - gets some data from the FIFO, no locking version
* Note that with only one concurrent reader and one concurrent
* writer, you don't need extra locking to use these functions.
*/
unsigned int __kfifo_get(struct kfifo *fifo,
unsigned char *buffer, unsigned int len)
{
unsigned int l;
len = min(len, fifo->in - fifo->out);
/* first get the data from fifo->out until the end of the buffer */
l = min(len, fifo->size - (fifo->out & (fifo->size - 1)));
memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), l);
/* then get the rest (if any) from the beginning of the buffer */
memcpy(buffer + l, fifo->buffer, len - l);
fifo->out += len;
return len;
}
orasy 2011-06-24
  • 打赏
  • 举报
回复
谢谢你的回复!
[Quote=引用 2 楼 shenyi0106 的回复:]

1.临界区不进行内核切换,全用户态操作
2.你的方法很好,基本上不需要做什么改动,当然也可以用队列,线程消息等

备注:最后,还得提醒一下,临界区不会进行内核切换,是所有同步中效率最高的一个
[/Quote]
orasy 2011-06-24
  • 打赏
  • 举报
回复
首先谢谢1楼的回复,没错,线程间同步必须耗费内核资源。我的程序是进程内的线程间同步。在B线程EnterCriticalSection 中,在这个等待过程中,是不是非常耗费的资源。有没有什么异动的方式。例如B线程注册了一个通知然后就"休眠"了,然后当A完成后,B也就收到这个通知,然后唤醒进行操作。不知道是否有这样的方式?
shenyi0106 2011-06-24
  • 打赏
  • 举报
回复
1.临界区不进行内核切换,全用户态操作
2.你的方法很好,基本上不需要做什么改动,当然也可以用队列,线程消息等

备注:最后,还得提醒一下,临界区不会进行内核切换,是所有同步中效率最高的一个
quwei197874 2011-06-24
  • 打赏
  • 举报
回复
线程同步都是有开销的,你的程序如果不是跨进程,用cs是开销最小的

15,471

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 进程/线程/DLL
社区管理员
  • 进程/线程/DLL社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧