现在在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下的多线程了解不多,不知道那种同步互斥方式做合适我的应用。谢谢!!!!

解决方案 »

  1.   

    首先谢谢1楼的回复,没错,线程间同步必须耗费内核资源。我的程序是进程内的线程间同步。在B线程EnterCriticalSection 中,在这个等待过程中,是不是非常耗费的资源。有没有什么异动的方式。例如B线程注册了一个通知然后就"休眠"了,然后当A完成后,B也就收到这个通知,然后唤醒进行操作。不知道是否有这样的方式?
      

  2.   

    你这种需求,采用一个循环缓冲就行了,只有一个线程写、一个线程读,可以采用无锁编程。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; 
     } 
      

  3.   

    我以前倒是看到过用volatile int 这个修饰符来定义一个环形buffer的读指针和写指针便宜量(据说读取这样的一个值是CPU的原子操作)。
    在你贴出的代码中,如果保证一个线程读fifo->out 和另外个线程修改这个值得矛盾呢?
      

  4.   

    临界区 等待共有对象时,会使得其他等待的线程进行挂起操作,只是这个挂起与唤醒调度是底层去完成,具体怎么调度很难讲吧,只是推荐不宜等待过久。做同步还有Interlocked系列的枷锁,是算原子操作了,但是用起来麻烦,不如临界区方便。
    只要尽量减少临界区中所等待的时间与加锁次数,就能提高同步效率。
      

  5.   


    即使写线程用来计算的读指针的值已经“过期”了,导致的结果也不过是写线程计算出的可用buffer空间比当前实际的小而已,但是绝对不会产生冲突的;反之读线程也一样。另外,临界区实现分两步,首先是利用类似Interlocked的原子操作实现在用户代码空间进入临界区,如果多次尝试不成功,就进入内核态,类似于WaitForSingleObject了
      

  6.   

    再说明下,在我说的应用中,A线程生成者预期是较耗时的(累积数据),而B消费者预期是较快的,那是否也就说明,B线程因为多次尝试进入临界区不成功,最后也是转为内核态了呢?
      

  7.   

    继续讨论。如果是这样的话,是否可以这样实现效率会好一些呢?创建一个Event,B(消费者)等待此event(等不到立即退出),如果等不到就sleep一段时间,再去等,直到等到为止,如果等到了,就尝试进入CS。生产者在将buffer填满后,设置event,并离开CS。这样的实现会比B一直等待进入CS更有效率吗?当然这样方式降低了实时性
      

  8.   

    在临界区上等待、WaitForSingleObject阻塞都不会消耗CPU资源的。不要2个缓冲切换,用循环缓冲。
    如果想简单,就用一个临界区就是效率最高的;如果想效率更高,就用无锁编程,其实循环缓冲你这种情况也没什么加锁的必要。我才做过相关项目,一个生产者线程不停往缓冲区写,一个线程当缓冲区满一定程度就往磁盘上写。可以明确的告诉你,多个缓冲切换非常的麻烦、非常的容易出错、非常的不易排查问题,解决好同步问题会花费你非常多的时间。
      

  9.   

    你说的那个项目和我说的应用要求比较类似,我再研究一下那个环形buffer的后,载请教。谢谢!!