使用线程间通信之条件变量

最近用C++写安卓下的一个通讯程序,作为jni库给java调用,采用多线程轮询遇到一个问题描述如下:
A线程收到数据,放入队列,是生产者。
B、C、D若干个线轮询训消息队列,如果队列有数据就取出进行处理,没数据就Sleep(T)休息,问题是这个T值取多大合适?取大了消息处理不及时,取小了手机cpu上升电池很快耗光。

这个问题最佳解法是采用条件变量,可以比较完美解决问题,以下代码使用C++封装,用win32 SDK的条件变量举例,Linux下有完全等价的概念:

// 线程消息通知
class ThreadMsgNotify
{
   // 条件变量和临界变量
   CONDITION_VARIABLE cv_;
   CRITICAL_SECTION cs_;
public:
   ThreadMsgNotify();
   ~ThreadMsgNotify();

   int Wait(DWORD ms);  // 消费者调用此函数,堵塞等待毫秒数
   void Notify();  //  生产者调用此函数: 发出通知
};

各成员方法实现如下:

// ---------------------------------------
ThreadMsgNotify::ThreadMsgNotify()
{
   InitializeConditionVariable(&cv_);
   InitializeCriticalSection(&cs_);
}

ThreadMsgNotify::~ThreadMsgNotify()
{
   WakeAllConditionVariable(&cv_);  // 唤醒全部线程
   Sleep(50);

   DeleteCriticalSection(&cs_);
   DeleteConditionVariable(&cv_);
}

int ThreadMsgNotify::Wait(DWORD ms)  // 消费者,堵塞等待毫秒数
{
   EnterCriticalSection(&cs_);
   int ret = SleepConditionVariableCS(&cv_, &cs_, ms);  // 等待
   LeaveCriticalSection(&cs_);
   return(ret);
}

void ThreadMsgNotify::Notify()  // 生产者: 发出通知
{
   EnterCriticalSection(&cs_);
   WakeConditionVariable(&cv_);  // 唤醒一个等待线程(如果有的话)
   LeaveCriticalSection(&cs_);
}
// --------------

上面的代码很简单,几乎不用解释。

下面再给出测试代码:

class TagThreadNotifyTest
{
  private:
     list<string> msgList;
     bool isEnd = false;
     CRITICAL_SECTION cs_;
  public:
     int no;
     ThreadMsgNotify threadNotify;  // 线程通知,接收队列收到消息时通过该对象唤醒处理线程

     TagThreadNotifyTest();
     ~TagThreadNotifyTest();

     void New(const char* info);  // 生产者增加一条消息
     int Recv(string& msg);  // 消费者读取一条消息,返回读取前队列中的消息数

     // 消费者线程函数
     static LRESULT WINAPI ProcThread(void* lParam);
};

// 辅助函数,获取当前时间戳
void CurTime(char* timeStr)
{
   SYSTEMTIME st;
   GetLocalTime(&st);
   sprintf(timeStr, "%02d:%02d:%02d.%03d", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
}

TagThreadNotifyTest::TagThreadNotifyTest()
{
  InitializeCriticalSection(&cs_);

  // 创建3个消费者线程
  for(int i=0; i<3; i++){
     no = i;
     DWORD dwThreadid;
     HANDLE thd = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ProcThread,
                (void*)this, 0, &dwThreadid);
     CloseHandle(thd);
     Sleep(30);
  }
}

TagThreadNotifyTest::~TagThreadNotifyTest()
{
  isEnd = true;
  Sleep(100);
  DeleteCriticalSection(&cs_);
}

// 生产者将新消息放入队列
void TagThreadNotifyTest::New(const char* info)
{
   // 加锁后插入队列
   EnterCriticalSection(&cs_);
      msgList.push_back(info);
   LeaveCriticalSection(&cs_);

   threadNotify.Notify();  // 通知其他线程,去处理数据

   printf("notify...\n");
}

// 消费者读取消息,如果没有将返回0
int TagThreadNotifyTest::Recv(string& msg)
{
   EnterCriticalSection(&cs_);
      int n = msgList.size();
      if( n>0 ){
          msg = msgList.front();
          msgList.pop_front();
      }
   LeaveCriticalSection(&cs_);
   return(n);
}

// 消费者线程
LRESULT WINAPI TagThreadNotifyTest::ProcThread(void* lParam)
{
  TagThreadNotifyTest * test = (TagThreadNotifyTest *)lParam;
  int no = test->no;
  printf("Thread start,  no=%d...\n", no);
  char timeStr[80];
  while( !test->isEnd ){
     string msg;
     int ret = test->Recv(msg);  // 读取一条消息
     CurTime(timeStr);
     if( ret ){  // 如果有就打印出来
        printf(" [%d %s]Recv: %s\n", no, timeStr, msg.c_str());
        Sleep(1000);  // 延时1秒模拟处理较慢的情况
        continue;
     }
     else{  // 没有收到
        printf(" [%d %s]...\n", no, timeStr);
     }
 
     // 休息15秒,如果有通知则会随时结束休息
     test->threadNotify.Wait(15000);
  }

  printf("Thread End : no=%d.\n", no);
  return(1);
}

int main()
{
   // 控制台测试程序

   // new一个测试对象,此对象会创建3个消费者线程
   TagThreadNotifyTest* test = new TagThreadNotifyTest();

   // 作为生产者线程,就是接收你的按键,回车后产生一条消息
   while(true){
       char s[500];
       memset(s, 0, 500);
       gets(s);
       if( strcmp(s, "exit")==0 ){
           break;
       }
       if( s[0]=='\0' )
           continue;

       // 提交消息
       test->New(s);
   }

   delete test;
   return(0);
}

在windows和Linux下(替换成对应函数)均测试通过。

输入一个回车后,消费者线程将立即取到消息并打印出来。如果没有消息,则消费者线程等待15秒,CPU很轻松。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,以下是一个使用互斥锁和条件变量实现线程间通信的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <pthread.h> #define BUFFER_SIZE 10 int buffer[BUFFER_SIZE]; // 共享缓冲区 int count = 0; // 缓冲区中元素个数 pthread_mutex_t mutex; // 互斥锁 pthread_cond_t cond; // 条件变量 void *producer(void *arg) { int item = 0; while (1) { pthread_mutex_lock(&mutex); // 加锁 if (count == BUFFER_SIZE) { // 缓冲区已满,等待消费者消费 pthread_cond_wait(&cond, &mutex); } buffer[count++] = item++; // 生产一个物品,放入缓冲区 printf("Producer produces item %d\n", item); pthread_mutex_unlock(&mutex); // 解锁 pthread_cond_signal(&cond); // 唤醒一个等待的消费者 } pthread_exit(NULL); } void *consumer(void *arg) { int item = 0; while (1) { pthread_mutex_lock(&mutex); // 加锁 if (count == 0) { // 缓冲区为空,等待生产者生产 pthread_cond_wait(&cond, &mutex); } item = buffer[--count]; // 消费一个物品,从缓冲区中取出 printf("Consumer consumes item %d\n", item); pthread_mutex_unlock(&mutex); // 解锁 pthread_cond_signal(&cond); // 唤醒一个等待的生产者 } pthread_exit(NULL); } int main() { pthread_t producer_tid, consumer_tid; // 初始化互斥锁和条件变量 pthread_mutex_init(&mutex, NULL); pthread_cond_init(&cond, NULL); // 创建生产者和消费者线程 pthread_create(&producer_tid, NULL, producer, NULL); pthread_create(&consumer_tid, NULL, consumer, NULL); // 等待线程结束 pthread_join(producer_tid, NULL); pthread_join(consumer_tid, NULL); // 销毁互斥锁和条件变量 pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond); return 0; } ``` 在上面的示例代码中,我们使用了一个缓冲区来实现生产者和消费者之间的通信。生产者将生产的物品放入缓冲区,消费者从缓冲区中取出物品进行消费。当缓冲区已满时,生产者会等待消费者消费;当缓冲区为空时,消费者会等待生产者生产。在等待的过程中,使用条件变量来进行线程的阻塞和唤醒。当生产者放入一个物品时,会唤醒一个等待的消费者;当消费者取出一个物品时,会唤醒一个等待的生产者。同时,使用互斥锁来保护共享缓冲区的访问,确保线程安全。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值