模板类高效线程安全的实现Queue

 保证同一时刻多个线程不会同时修改同一个共享资源,那么这个程序是线程安全的,或者是串行化访问资源的。可以使用mutex类来控制线程的并发问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <iostream>

#include <boost/thread/thread.hpp>

#include <string>
 
// A simple queue class; don't do this, use std::queue

template<typename T>

class Queue {

public:

   Queue( ) {}

  ~Queue( ) {}
 
   void enqueue(const T& x) 
   {
      // Lock the mutex for this queue

      boost::mutex::scoped_lock lock(mutex_);

      list_.push_back(x);

      // A scoped_lock is automatically destroyed (and thus unlocked)

      // when it goes out of scope
    } 
 
   T dequeue( ) {

      boost::mutex::scoped_lock lock(mutex_);
 
      if (list_.empty( ))
         throw "empty!";     // This leaves the current scope, so the

      T tmp = list_.front( ); // lock is released

      list_.pop_front( );

      return(tmp);

   } // Again: when scope ends, mutex_ is unlocked
 
private:

   std::list<T> list_;

   boost::mutex mutex_;
};
 
Queue<std::string> queueOfStrings;
 
void sendSomething( ) {

   std::string s;

   for (int i = 0; i < 10; ++i) 
   {
      queueOfStrings.enqueue("Cyrus");
   }
}
 
void recvSomething( ) {

   std::string s;
 
   for (int i = 0; i < 10; ++i) {

      try {s = queueOfStrings.dequeue( );}
      catch(...) {}

   }
}
 
int main( ) {

   boost::thread thr1(sendSomething);

   boost::thread thr2(recvSomething);
 
   thr1.join( );

   thr2.join( );
}

    mutex对象本身并不知道它代表什么,它仅仅是被多个消费者线程使用的资源访问的锁定解锁标志。在某个时刻,只有一个线程可以锁定这个mutex对象,这就阻止了同一时刻有多个线程并发访问共享资源。一个mutex就是一个简单的信号机制。

    给mutex加解锁有多种策略,最简单的是使用scoped_lock类,它使用一个mutex参数来构造,并一直锁定这个mutex直到对象被销毁。如果这个正在被构造的mutex已经被别的线程锁定的话,当前线程就会进入wait状态,直到这个锁被解开。

利用read_write_mutex对上述的进行改进:

mutex有一个美中不足,它不区分读和写。线程如果只是进行读操作,mutex强制线程串行化访问资源,效率低。而且这种操作不需要排他性访问。基于这个原因,Boost线程库提供了read_write_mutex。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <iostream>
#include <boost/thread/thread.hpp>
#include <boost/thread/read_write_mutex.hpp>
#include <string>
 
template<typename T>
class Queue {
public:
   Queue( ) :  // Use a read/write mutex and give writers priority
      rwMutex_(boost::read_write_scheduling_policy::writer_priority){}
  ~Queue( ) {}
 
   void enqueue(const T& x) {
      // Use a r/w lock since enqueue updates the state
      boost::read_write_mutex::scoped_write_lock writeLock(rwMutex_);
      list_.push_back(x);
   } 
 
   T dequeue( ) {
      // Again, use a write lock
      boost::read_write_mutex::scoped_write_lock writeLock(rwMutex_);
 
      if (list_.empty( ))
         throw "empty!";
      T tmp = list_.front( );
      list_.pop_front( );
      return(tmp);
   }
 
   T getFront( ) {
      // This is a read-only operation, so you only need a read lock
      boost::read_write_mutex::scoped_read_lock readLock(rwMutex_);
      if (list_.empty( ))
         throw "empty!";
      return(list_.front( ));
   }
 
private:
   std::list<T> list_;
   boost::read_write_mutex rwMutex_;
};
 
Queue<std::string> queueOfStrings;
 
void sendSomething( ) {
   std::string s;
 
   for (int i = 0; i < 10; ++i) {
      queueOfStrings.enqueue("Cyrus");
   }
}
 
void checkTheFront( ) {
   std::string s;
 
   for (int i = 0; i < 10; ++i) {
      try {s = queueOfStrings.getFront( );}
      catch(...) {}
   }
}
 
int main( ) {
 
   boost::thread thr1(sendSomething);
   boost::thread_group grp;
 
   grp.create_thread(checkTheFront);
   grp.create_thread(checkTheFront);
   grp.create_thread(checkTheFront);
   grp.create_thread(checkTheFront);
 
   thr1.join( );
   grp.join_all( );
}

     注意Queue的构造函数中队读写锁rwMutex的初始化。同一时刻,可能有多个读写线程要锁定一个read_write_mutex,而这些锁的调度策略依赖于构造这个mutex时选定的调度策略。Boost库中提供了四种调度策略:

1)reader_priority:等待读锁的线程优先于等待写锁的线程

2)writer_priority:等待写锁的线程优先于等待读锁的线程

3)alternating_single_read:在读锁和写锁之间交替

4)alternating_many_reads:在读锁和写锁之间交替,这个策略将在两个写锁之间使得所有的在这个queue上挂起的读锁都被允许。

     选择使用哪种策略要慎重,因为使用前两种的话可能会导致某些锁始终不能成功,出现饿死的现象。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值