c++的boost庫多線程(Thread)編程(線程操做,互斥體mutex,條件變量)詳解html
Boost是由C++標準委員會類庫工做組成員發起,致力於爲C++開發新的類庫的組織。許多C++專家都投身於Boost線程庫的開發中。全部接口的設計都是從0開始的,並非C線程API的簡單封裝。許多C++特性(好比構造函數和 析構函數,函數對象(function object)和模板)都被使��在其中以使接口更加靈活。現ios
在的版本能夠在POSIX,Win32和Macintosh Carbon平臺下工做。c++
1 boost庫建立線程編程
就像std::fstream類就表明一個文件同樣,boost::thread類就表明一個可執行的線程。缺省構造函數建立一個表明當前執行線程的實 例。一個重載的構緩存
造函數以一個不需任何參數的函數對象做爲參數,而且沒有返回值。這個構造函數建立一個新的可執行線程,它調用了那個函數對象。安全
起先,你們認爲傳統C建立線程的方法彷佛比這樣的設計更有用,由於C建立線程的時候會傳入一個void*指針,經過這種方法就能夠傳入數據。然而, 由網絡
於Boost線程庫是使用函數對象來代替函數指針,那麼函數對象自己就能夠攜帶線程所需的數據。這種方法更具靈活性,也是類型安全(type- safe)的。當和多線程
Boost.Bind這樣的功能庫一塊兒使用時,這樣的方法就可讓你傳遞任意數量的數據給新建的線程。目前,由Boost線程庫建立的線程對象功能還不是很強大。事併發
實上它只能作兩項操做。線程對象能夠方便使用==和!=進行比較來肯定它們是不是表明 同一個線程;你還能夠調用boost::thread::join來等待線程執行完畢函數
。其餘一些線程庫可讓你對線程作一些其餘操做(好比設置優先級,甚 至是取消線程)。然而,因爲要在廣泛適用(portable)的接口中加入這些操做不是
簡單的事,目前仍在討論如何將這些操組加入到Boost線程庫中。
boost庫多線程(Thread)用法舉例1:
1
2
3
4
5
6
7
8
9
10
11
12
|
#include <boost/thread/thread.hpp>
#include <iostream>
void
hello()
{
std::cout <<
"Hello world, I'm a thread!"
<< std::endl;
}
int
main(
int
argc,
char
* argv[])
{
boost::
thread
thrd(&hello);
thrd.join();
return
0;
}
|
2 boost庫多線程中的互斥體
任何寫過多線程程序的人都知道避免不一樣線程同時訪問共享區域的重要性。若是一個 線程要改變共享區域中某個數據,而與此同時另外一線程正在讀這個數
據,那麼結果將是未定義的。爲了不這種狀況的發生就要使用一些特殊的原始類型和操做。其中最基本的就是互斥體(mutex,mutual exclusion的縮寫)。
一個互斥體一次只容許一個線程訪問共享區。當一個線程想要訪問共享區時,首先要作的就是鎖住(lock)互斥體。若是其餘的 線程已經鎖住了互斥體,那麼
就必須先等那個線程將互斥體解鎖,這樣就保證了同一時刻只有一個線程能訪問共享區域。
互斥體的概念有很多變種。Boost線程庫支持兩大類互斥體,包括簡單互斥體(simple mutex)和遞歸互斥體(recursive mutex)。若是同一個線程對互斥體上了兩次鎖,就會發生死鎖(deadlock),也就是說全部的等待解鎖的線程將一直等下去。有了遞歸互斥體,單個 線程就能夠對互斥體屢次上鎖,固然也必須解鎖一樣次數來保證其餘線程能夠對這個互斥體上鎖。
在這兩大類互斥體中,對於線程如何上鎖還有多個變種。一個線程能夠有三種方法來對一個互斥體加鎖:
一直等到沒有其餘線程對互斥體加鎖。
若是有其餘互斥體已經對互斥體加鎖就當即返回。
一直等到沒有其餘線程互斥體加鎖,直到超時。
彷佛最佳的互斥體類型是遞歸互斥體,它可使用全部三種上鎖形式。然而每個變種都是有代價的。因此Boost線程庫容許你根據不一樣的須要使用最有效率的互斥體類型。Boost線程庫提供了6中互斥體類型,下面是按照效率進行排序:
boost::mutex,
boost::try_mutex,
boost::timed_mutex,
boost::recursive_mutex,
boost::recursive_try_mutex,
boost::recursive_timed_mutex
若是互斥體上鎖以後沒有解鎖就會發生死鎖。這是一個很廣泛的錯誤,Boost線程庫就是要將其變成不可能(至少時很困難)。直接對互斥體上鎖和解鎖對於
Boost線程庫的用戶來講是不可能的。mutex類經過teypdef定義在RAII中實現的類型來實現互斥體的上鎖和解鎖。這也就是你們知道的 Scope Lock模式。爲了
構造這些類型,要傳入一個互斥體的引用。構造函數對互斥體加鎖,析構函數對互斥體解鎖。C++保證了析構函數必定會被調用,因此即便是 有異常拋出,互
斥體也老是會被正確的解鎖。這種方法保證正確的使用互斥體。然而,有一點必須注意:儘管Scope Lock模式能夠保證互斥體被解鎖,可是它並無保證在異常拋出以後貢獻資源還是可用的。因此就像執行單線程程序同樣,必須保證異常不會致使程序狀態異 常。另外,這個已經上鎖的對象不能傳遞給另外一個線程,由於它們維護的狀態並無禁止這樣作。
例2給出了一個使用boost::mutex的最簡單的例子。例子中共建立了兩個新的線程,每一個線程都有10次循環,在std::cout上 打印出線程id和當前循環的次數,而main函數等待這兩個線程執行完才結束。std::cout就是共享資源,因此每個線程都使用一個全局互斥體來保 證同時只有一個線程能向它寫入。
許多讀者可能已經注意到例2中傳遞數據給線程還必須的手工寫一個函數。儘管這個例子很簡單,若是每一次都要寫這樣的代碼實在是讓人厭煩的事。 別急
,有一種簡單的解決辦法。函數庫容許你經過將另外一個函數綁定,並傳入調用時須要的數據來建立一個新的函數。 例3向你展現瞭如何使用Boost.Bind庫來
簡化List2中的代碼,這樣就沒必要手工寫這些函數對象了。
boost庫多線程(Thread)互斥體(mutex)用法舉例2:
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
|
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <iostream>
boost::mutex io_mutex;
struct
count
{
count(
int
id) : id(id) { }
void
operator()()
{
for
(
int
i = 0; i < 10; ++i)
{
boost::mutex::scoped_lock
lock(io_mutex);
std::cout << id <<
": "
<< i << std::endl;
}
}
int
id;
};
int
main(
int
argc,
char
* argv[])
{
boost::
thread
thrd1(count(1));
boost::
thread
thrd2(count(2));
thrd1.join();
thrd2.join();
return
0;
}
|
boost庫多線程(Thread)互斥體(mutex)用法舉例3: // 這個例子和例2同樣,除了使用Boost.Bind來簡化建立線程攜帶數據,避免使用函數對象
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
|
//文章來源:www.169it.com
#include <boost/thread/mutex.hpp>
#include <boost/bind.hpp>
#include <iostream>
boost::mutex io_mutex;
void
count(
int
id)
{
for
(
int
i = 0; i < 10; ++i)
{
boost::mutex::scoped_lock
lock(io_mutex);
std::cout << id <<
": "
<<
i << std::endl;
}
}
int
main(
int
argc,
char
* argv[])
{
boost::
thread
thrd1(
boost::bind(&count, 1));
boost::
thread
thrd2(
boost::bind(&count, 2));
thrd1.join();
thrd2.join();
return
0;
}
|
3 boost庫多線程之條件變量
有的時候僅僅依靠鎖住共享資源來使用它是不夠的。有時候共享資源只有某些狀態的時候纔可以使用。比方說,某個線程若是要從堆棧中讀取數據,那麼若是棧中沒 有數據就必須等待數據被壓棧。這種狀況下的同步使用互斥體是不夠的。另外一種同步的方式--條件變量,就可使用在這種狀況下。
條件變量的使用老是和互斥體及共享資源聯繫在一塊兒的。線程首先鎖住互斥體,而後檢驗共享資源的狀態是否處於可以使用的狀態。若是不是,那麼線程就要等 待條件變量。要指向這樣的操做就必須在等待的時候將互斥體解鎖,以便其餘線程能夠訪問共享資源並改變其狀態。它還得保證從等到得線程返回時互斥體是被上鎖 得。當另外一個線程改變了共享資源的狀態時,它就要通知正在等待條件變量得線程,並將之返回等待的線程。
例4是一個使用了boost::condition的簡單例子。有一個實現了有界緩存區的類和一個固定大小的先進先出的容器。因爲使用了互斥 體boost::mutex,這個
緩存區是線程安全的。put和get使用條件變量來保證線程等待完成操做所必須的狀態。有兩個線程被建立,一個在 buffer中放入100個整數,另外一個將它們從
buffer中取出。這個有界的緩存一次只能存放10個整數,因此這兩個線程必須週期性的等待另外一個線 程。爲了驗證這一點,put和get在std::cout中輸出診斷
語句。最後,當兩個線程結束後,main函數也就執行完畢了。
boost庫多線程(Thread)添加變量用法例4
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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition.hpp>
#include <iostream>
const
int
BUF_SIZE = 10;
const
int
ITERS = 100;
boost::mutex io_mutex;
class
buffer
{
public
:
typedef
boost::mutex::scoped_lock
scoped_lock;
buffer()
: p(0), c(0), full(0)
{
}
void
put(
int
m)
{
scoped_lock lock(mutex);
if
(full == BUF_SIZE)
{
{
boost::mutex::scoped_lock
lock(io_mutex);
std::cout <<
"Buffer is full. Waiting..."
<< std::endl;
}
while
(full == BUF_SIZE)
cond.wait(lock);
}
buf[p] = m;
p = (p+1) % BUF_SIZE;
++full;
cond.notify_one();
}
int
get()
{
scoped_lock lk(mutex);
if
(full == 0)
{
{
boost::mutex::scoped_lock
lock(io_mutex);
std::cout <<
"Buffer is empty. Waiting..."
<< std::endl;
}
while
(full == 0)
cond.wait(lk);
}
int
i = buf[c];
c = (c+1) % BUF_SIZE;
--full;
cond.notify_one();
return
i;
}
private
:
boost::mutex mutex;
boost::condition cond;
unsigned
int
p, c, full;
int
buf[BUF_SIZE];
};
buffer buf;
void
writer()
{
for
(
int
n = 0; n < ITERS; ++n)
{
{
boost::mutex::scoped_lock
lock(io_mutex);
std::cout <<
"sending: "
<< n << std::endl;
}
buf.put(n);
}
}
void
reader()
{
for
(
int
x = 0; x < ITERS; ++x)
{
int
n = buf.get();
{
boost::mutex::scoped_lock
lock(io_mutex);
std::cout <<
"received: "
<< n << std::endl;
}
}
}
int
main(
int
argc,
char
* argv[])
{
boost::
thread
thrd1(&reader);
boost::
thread
thrd2(&writer);
thrd1.join();
thrd2.join();
return
0;
}
|
4 boost庫多線程(Thread)之線程局部存儲
大多數函數都不是可重入的。這也就是說在某一個線程已經調用了一個函數時,若是你再調用同一個函數,那麼這樣是不安全的。一個不可重入的函數經過連續的調 用來保存靜態變量或者是返回一個指向靜態數據的指針。 舉例來講,std::strtok就是不可重入的,由於它使用靜態變量來保存要被分割成符號的字符串。
有兩種方法可讓不可重用的函數變成可重用的函數。第一種方法就是改變接口,用指針或引用代替原先使用靜態數據的地方。比方說,POSIX定義了 strok_r,std::strtok中的一個可重入的變量,它用一個額外的char**參數來代替靜態數據。這種方法很簡單,並且提供了可能的最佳效 果。可是這樣必須改變公共接口,也就意味着必須改代碼。另外一種方法不用改變公有接口,而是用本地存儲線程(thread local storage)來代替靜態數據(有時也被成爲特殊線程存儲,thread-specific storage)。
Boost線程庫提供了智能指針boost::thread_specific_ptr來訪問本地存儲線程。每個線程第一次使用這個智能指針的實 例時,它的初值是NULL,因此必須要先檢查這個它的只是否爲空,而且爲它賦值。Boost線程庫保證本地存儲線程中保存的數據會在線程結束後被清除。
例5是一個使用boost::thread_specific_ptr的簡單例子。其中建立了兩個線程來初始化本地存儲線程,並有10次循 環,每一次都會增長智能指針指向的值,並將其輸出到std::cout上(因爲std::cout是一個共享資源,因此經過互斥體進行同步)。main線 程等待這兩個線程結束後就退出。從這個例子輸出能夠明白的看出每一個線程都處理屬於本身的數據實例,儘管它們都是使用同一個 boost::thread_specific_ptr。
boost庫多線程線程局部存儲例5:
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
|
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/tss.hpp>
#include <iostream>
boost::mutex io_mutex;
boost::thread_specific_ptr<
int
> ptr;
struct
count
{
count(
int
id) : id(id) { }
void
operator()()
{
if
(ptr.get() == 0)
ptr.reset(
new
int
(0));
for
(
int
i = 0; i < 10; ++i)
{
(*ptr)++;
boost::mutex::scoped_lock
lock(io_mutex);
std::cout << id <<
": "
<< *ptr << std::endl;
}
}
int
id;
};
int
main(
int
argc,
char
* argv[])
{
boost::
thread
thrd1(count(1));
boost::
thread
thrd2(count(2));
thrd1.join();
thrd2.join();
return
0;
}
|
5 boost庫多線程(Thread)編程知識點總結
頭文件:#include<boost/thread.hpp>
1) 線程建立,管理
thread類
(1)線程構造函數:
boost::thread(Callable func);
(2)帶參數函數對應的線程構造函數:
boost::thread(F f,A1 a1,A2 a2,...); //目前最多支持帶九個額外參數(a1 到 a9 )的f。
(3)成員函數join()
阻塞調用當前線程的線程,直到當前線程執行完畢。
(4)成員函數get_id()
返回當前線程的ID。
(5)成員函數hardware_concurrency()
返回當前線程數。
獨立於thread類的this_thread命名空間
(1)boost::this_thread::get_id
同上get_id
(2)boost::this_thread::sleep
將調用線程掛起指定時間。例:boost::this_thread::sleep(boost::posix_time::seconds(seconds));
2) 同步
使用線程同時執行幾個函數時,訪問共享資源(全局變量等)時要考慮同步,同步所需工做以下:
(1)互斥量
互斥的原則是當線程擁有共享資源時防止其餘線程奪取其全部權,一旦釋放,其餘線程能夠取得全部權,這將致使線程等待另外一個線程處理完一些操做,釋放互斥量的全部權。
獨佔式互斥量:boost::mutex ,併發調用成員函數lock(),unlock()
multiple-reader / single-writer互斥量:boost::shared_mutex,併發調用成員函數lock(),unlock()
(2)更多功能的鎖
boost::unique_lock 獨佔鎖
boost::shared_lock multiple-reader / single-writer鎖
3) 死鎖
死鎖主要發生在有多個依賴鎖存在時,會在一個線程試圖與另外一個線程以相反的順序鎖住互斥量。如何避免死鎖:
(1)對共享資源操做前,必定要得到鎖;
(2)完成操做後,必定要釋放鎖;
(3)儘可能短時佔用鎖;
(4)若是有多鎖,如得到順序是ABC,則釋放順序也是ABC。
(5)若是線程錯誤返回,應該釋放他所得到的鎖。
本站文章除註明原創外,均爲轉載,整理或蒐集自網絡.歡迎任何形式的轉載,轉載請註明出處.
轉載請註明:文章轉載自:[169it-最新IT科技資訊]
本文標題:c++的boost庫多線程(Thread)編程(線程操做,互斥體mutex,條件變量)詳解