一、创建一个线程

创建线程

boost::thread myThread(threadFun);

需要注意的是:参数可以是函数对象或者函数指针。并且这个函数无参数,并返回void类型

当一个thread执行完成时,这个子线程就会消失。注意这个线程对象不会消失,它仍然是一个还处在它的生存期的C++对象。同理,当对一个堆上的线程对象的指针调用delete时候,线程对象被销毁,操作系统的线程并不能保证就消失。

放弃时间片

boost::thread::yield();

当前线程放弃余下的时间片。

等待一个线程

myThread.join();

调用这个方法的线程进入wait状态,直到myThread代表的线程完成为止。如果它不结束的话,join方法就不会返回。join是一个等待子线程结束的最好的方法。如果主程序不调用join方法而直接结束,它的子线程有可能没有执行完成,但是所有的子线程也随之退出。不调用join方法,主线程就不会等待它的子线程。

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

#include <iostream>

#include <boost/thread/thread.hpp>

#include <boost/thread/xtime.hpp>

struct MyThreadFunc {

void operator( )( ) {

// Do something long-running...

}

} threadFun;

int main( ) {

boost::thread myThread(threadFun); // Create a thread that starts

// running threadFun

boost::thread::yield( );           // Give up the main thread's timeslice

// so the child thread can get some work

// done.

// Go do some other work...

myThread.join( );                  // The current (i.e., main) thread will wait

// for myThread to finish before it returns

}

线程组

如果你需要创建几个线程,考虑使用一个线程组对象thread_group来组织它们。一个thread_group对象可以使用多种方法管理线程。首先,可以使用一个指向动态创建的线程对象的指针作为参数来调用add_thread方法,将这个线程加入线程组。也可以直接使用线程组类的create_thread方法,可不先创建线程而直接把线程加入到线程组中。

当线程组对象的析构函数被调用时,它将删除(delete)所有这些通过add_thread方法加入的线程指针。所以,只能将堆上的线程对象指针通过add_thread方法加入线程组。remove_thread方法从线程组删除某个线程的指针,但是我们仍需负责把线程本身内存释放掉。

线程组对象的成员方法join_all方法等待线程组中所有线程结束,才返回。

1

2

3

4

5

6

7

8

9

10

boost::thread_group grp;

boost::thread *p = new boost::thread(threadFun);

grp.add_thread(p);

//do something...

grp.remove_thread(p);

grp.create_thread(threadFun);

grp.create_thread(threadFun);   //Now there are two threads in grp

grp.join_all();                 //Wait for all threads to finish

二、使资源是线程安全的

    保证同一时刻多个线程不会同时修改同一个共享资源,那么这个程序是线程安全的,或者是串行化访问资源的。可以使用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状态,直到这个锁被解开。

三、读写锁

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上挂起的读锁都被允许。

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

死锁、饿死和竞态条件

1)死锁,是涉及至少2个线程和2个资源的情况。线程A和B,资源X和Y。A锁定了X,而B锁定了Y。此时A和B有彼此想要对方的资源,死锁就出现了。

死锁的预防有两种方法。一种是,通过小心的按照一定的顺序对不同的mutex来加锁。另一种是,使用Boost提供的try_mutex互斥量和scoped_try_lock。或者使用时间锁。scoped_try_lock对try_mutex加锁时,可能成功,也可能失败,但不会阻塞。时间锁则有一个超时时间。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

bool dequeue(T& x)

{

boost::try_mutex::scope_try_lock lock(tryMutex_);

if(!lock.locked())

return false;

else{

if (list_.empty())

throw "empty!";

x = list_.front();

list_.pop_front();

return true;

}

}

private:

boost::try_mutex tryMutex_;

2)饿死,如果你正在使用write_priority策略,并且你有很多创建写锁的线程,那么读锁的线程就可能饿死。

3)竞态条件,

1

2

3

4

if(q.getFront() == "Cyrus"){

str = q.dequeue();

//....

}

这个代码在单线程环境中工作很好,因为q在第一行和第二行代码之间不会被修改。多线程环境中则会出现问题。此为竞态条件。解决的方法是为Queue添加一个成员函数dequeueIfEquals,在函数执行过程中始终锁定互斥量。

四、从一个线程中给另一个线程发送通知

当需要线程等待某个事物时,可以创建一个condition对象,然后通过这个对象来通知那些等待的线程。

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

#include <iostream>

#include <boost/thread/thread.hpp>

#include <boost/thread/condition.hpp>

#include <boost/thread/mutex.hpp>

#include <list>

#include <string>

class Request { /*...*/ };

// A simple job queue class; don't do this, use std::queue

template<typename T>

class JobQueue {

public:

JobQueue( ) {}

~JobQueue( ) {}

void submitJob(const T& x) {

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

list_.push_back(x);

workToBeDone_.notify_one( );

}

T getJob( ) {

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

workToBeDone_.wait(lock); // Wait until this condition is

// satisfied, then lock the mutex

T tmp = list_.front( );

list_.pop_front( );

return(tmp);

}

private:

std::list<T> list_;

boost::mutex mutex_;

boost::condition workToBeDone_;

};

JobQueue<Request> myJobQueue;

void boss( ) {

for (;;) {

// Get the request from somewhere

Request req;

myJobQueue.submitJob(req);

}

}

void worker( ) {

for (;;) {

Request r(myJobQueue.getJob( ));

// Do something with the job...

}

}

int main( ) {

boost::thread thr1(boss);

boost::thread thr2(worker);

boost::thread thr3(worker);

thr1.join( );

thr2.join( );

thr3.join( );

}

boost::mutex::scoped_locklock(mutex_);

workToBeDone_.wait(lock);

这两行代码,第一行锁定这个mutex对象。第二行代码解开这个mutex上的锁,然后进行等待或者休眠,直到它的条件得到了满足。这个mutex互斥对象的解锁让其他的线程能够使用这个mutex对象,它们中的某个需要设置这个等待条件,之后通知另外的线程。

notify_all函数,通知那些所有正在等待某个条件变为真的线程,那些线程随后进入运行状态。wait方法做两件事情:它一直等待直到有人在它正等待的condition上调用notify_one或notify_all,然后它就试图锁定相关的mutex。当调用的是notify_all时,尽管多个等待的线程都尽量去获得下一个锁,但谁将获得依赖于这个mutex的类型和使用的优先策略。

一个condition对象能让消费者线程休眠,因此在还没有碰到一个condition时处理器可以去处理别的事情。例如一个web服务器使用一个工作线程池来处理进来的请求。当没有需求进来时,让这些子线程处于等待状态比让它们循环的查询或者睡眠然后偶尔唤醒来检查这个队列,要好很多。

五、只初始化一次共享资源

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

#include <iostream>

#include <boost/thread/thread.hpp>

#include <boost/thread/once.hpp>

// Some sort of connection class that should only be initialized once

struct Conn {

static void init( ) {++i_;}

static boost::once_flag init_;

static int i_;

// ...

};

int Conn::i_ = 0;

boost::once_flag Conn::init_ = BOOST_ONCE_INIT;

void worker( ) {

boost::call_once(Conn::init, Conn::init_);

// Do the real work...

}

Conn c;  // You probably don't want to use a global, so see the

// next Recipe

int main( ) {

boost::thread_group grp;

for (int i = 0; i < 100; ++i)

grp.create_thread(worker);

grp.join_all( );

std::cout << c.i_ << '\n'; // c.i_ = 1

}

一个共享资源不得不在某个地方被初始化,并且你希望第一次使用这个资源的线程来完成初始化工作。一个once_flag类型和call_once函数能够保证多个线程不会重复的初始化同一个对象。首先,必须使用BOOST_ONCE_INIT宏来初始化这个once_flag对象。boost::once_flag Conn::init_ =BOOST_ONCE_INIT; 之后调用call_once函数,boost::call_once(Conn::init, Conn::init_); 第一个形参是希望被执行一次的初始化函数的地址。

六、给线程函数传递一个参数

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

#include <iostream>

#include <string>

#include <functional>

#include <boost/thread/thread.hpp>

// A typedef to make the declarations below easier to read

typedef void (*WorkerFunPtr)(const std::string&);

template<typename FunT,   // The type of the function being called

typename ParamT> // The type of its parameter

struct Adapter {

Adapter(FunT f, ParamT& p) : // Construct this adapter and set the

f_(f), p_(&p) {}          // members to the function and its arg

void operator( )( ) { // This just calls the function with its arg

f_(*p_);

}

private:

FunT    f_;

ParamT* p_;  // Use the parameter's address to avoid extra copying

};

void worker(const std::string& s) {

std::cout << s << '\n';

}

int main( ) {

std::string s1 = "This is the first thread!";

std::string s2 = "This is the second thread!";

boost::thread thr1(Adapter<WorkerFunPtr, std::string>(worker, s1));

boost::thread thr2(Adapter<WorkerFunPtr, std::string>(worker, s2));

thr1.join( );

thr2.join( );

}

使用这个函数适配器类模板,你就可以给线程函数传递参数了。如果你需要传递多个参数,仅需要在这个适配器中增加另一个类型和成员变量。

boost thread使用方法相关推荐

  1. C++ Boost Thread 编程指南

    http://www.cppblog.com/shaker/archive/2007/10/06/33583.html 0 前言 标准C++线程即将到来.CUJ预言它将衍生自Boost线程库,现在就由 ...

  2. boost::thread编程实战(2)——获取线程id并转化为DWORD类型

    1. boost::thread::id 的基础知识 ① boost::thread::id 的对象能够用来标识线程,每个正在运行的线程都可以获取一个唯一可用的线程id,可以通过调用 boost::t ...

  3. BOOST THREAD

    转载:http://www.blogjava.net/LittleDS/category/31585.html Boost Thread学习笔记 thread自然是boost ::thread库的主 ...

  4. 从一道面试题分析Thread.interrupt方法

    阿里面试题: public class TestThread {public static void main(String[] args) {Thread t1 = new Thread() {@O ...

  5. java threadgourp_Java Thread getThreadGroup()方法

    Java Thread getThreadGroup()方法 java.lang.Thread.getThreadGroup() 方法返回此线程所属的线程组.它返回null,如果该线程已经死亡(停止) ...

  6. C++ boost thread学习(二)

    条件变量 如果线程之间执行顺序上有依赖关系,可使用条件变量(Condition variables). 可以到boost官网中参考条件变量(Condition variables)的使用. 条件变量必 ...

  7. libreportparam2.dll无法继续执行代码_Java并发系列番外(1)——让Thread#stop方法无法终止你的线程...

    众所周知,Thread 类有一个 stop 方法,可以用来终止线程.当然,这个方法连同 suspend,resume 方法一起都已经被弃用了.原因是 stop 方法终止线程过于暴力,不管被 stop ...

  8. boost::thread模块实现生产者消费者的测试程序

    boost::thread模块实现生产者消费者的测试程序 实现功能 C++实现代码 实现功能 boost::thread模块实现生产者消费者的测试程序 C++实现代码 #include <boo ...

  9. boost::thread模块实现默认执行器的测试程序

    boost::thread模块实现默认执行器的测试程序 实现功能 C++实现代码 实现功能 boost::thread模块实现默认执行器的测试程序 C++实现代码 #include <boost ...

最新文章

  1. android 视频沉浸式,Android项目实战(四十一):游戏和视频类型应用 状态栏沉浸式效果...
  2. linux中locate find 与 grep
  3. 左神算法:用递归函数和栈逆序一个栈(Java版)
  4. 【剑指offer】面试题21:调整数组顺序使奇数位于偶数前面(Java)
  5. python做一个窗口样式_python3+PyQt5 自定义窗口部件--使用窗口部件样式表的方法...
  6. python抖音github_使用 Python 下载抖音无水印视频
  7. (6)Vivado软件开发流程(第2天)
  8. 教学计划计算机,计算机教学计划模板
  9. Linux内存管理之页面回收
  10. 如何从尚硅谷下载免费的前端开发视频资源
  11. Hightopo 受邀参加第二十三届中国国际高新技术成果交易会
  12. VSCode无法跳转到外部函数定义
  13. js 获取指定日期的前几天日期或后几天日期
  14. 如何系统的学习linux
  15. 生成树协议三姐妹:STP、RSTP 和 MSTP,附思科和华为双厂商命令示例
  16. 网页链接跳转qq聊天界面以及QQ群是什么实现的
  17. win10系统获取管理员权限的设置方法一览
  18. html5 拓扑图 切片,HTML5复杂拓扑图(四) 组织结构图
  19. OpenCV—python 图像相似度算法(dHash,方差)
  20. HBase学习提纲:助你一臂之力

热门文章

  1. c语言指针,return以及函数返回值
  2. 经典九大排序(1)——简单排序
  3. wm wap 抓包办法
  4. 武林外传打怪 优化版
  5. 代码随想录算法训练营第30天| 332.重新安排行程 、51. N皇后 、 37. 解数独
  6. 层次分析法(多准则决策方法)
  7. python在import库的时候报错OSError:不是有效的win32应用程序
  8. 巨人推进“网游社区化” 或参股51.COM
  9. jquery动画(1)
  10. excel 区间人数柱状图_Excel图表教程:区间柱状图