Posix執行緒和C++11多執行緒學習
在C++11引進多執行緒之前,我們不得不使用POSIX pthreads,因此本文主要包括三部分:
- POSIX多執行緒實踐
- C++11 多執行緒實踐
- 類成員函式作為執行緒函式的實現
一、POSIX多執行緒實踐
一個簡單執行緒的實現
建立一個執行緒,該執行緒是joinable的,因此pthread_join阻塞,等待執行緒執行完畢。
//Create a Posix thread from the main program #include <iostream> #include <pthread.h> //This function will be called from a thread void *call_from_thread(void *) { std::cout << "Launched by thread" << std::endl; return NULL; } int main() { pthread_t t; //Launch a thread pthread_create(&t, NULL, call_from_thread, NULL); //Join the thread with the main thread pthread_join(t, NULL); return 0; }
$ g++ posix_thread_00.cpp -o posix_thread_00 -lpthread
$ ./posix_thread_00
Launched by thread
多個執行緒的實現
//Create a group of Posix threads from the main program
#include <iostream>
#include <pthread.h>
static const int num_threads = 5;
//This function will be called from a thread
void *call_from_thread (void *) {
std::cout << "Launched by thread:"<<pthread_self()<<std::endl;
return NULL;
}
int main() {
pthread_t t[num_threads];
//Launch a group of threads
for (int i = 0; i < num_threads; ++i) {
pthread_create(&t[i], NULL, call_from_thread, NULL);
}
std::cout << "Launched from the main\n";
//Join the threads with the main thread
for (int i = 0; i < num_threads; ++i) {
pthread_join(t[i], NULL);
}
return 0;
}
$ g++ posix_thread_01.cpp -o posix_thread_01 -lpthread
$ ./posix_thread_01
Launched by thread:140300023379712
Launched by thread:140300014987008
Launched by thread:140300006594304
Launched by thread:140299998201600
Launched from the main
Launched by thread:140300031772416
當建立多個子執行緒時,外加主執行緒,共計6個執行緒,我們可以看到執行緒之間是平等競爭臨界資源關係,順序是無序的,因此如果要明確確定順序,則需要同步等barrier術語來改進。
執行緒傳參的實現
執行緒的傳參可以是簡單的資料型別,也可以是自定義的資料結構,因此我們在編寫執行緒傳參的時候要留意這些。
//Create a group of Posix threads from the main program
#include <iostream>
#include <pthread.h>
static const int num_threads = 5;
typedef struct {
int thread_id;
} thread_data;
//This function will be called from a thread
void *call_from_thread(void *args) {
thread_data *my_data = (thread_data *) args;
std::cout << "Launched by thread " << my_data->thread_id << std::endl;
return NULL;
}
int main() {
pthread_t t[num_threads];
thread_data td[num_threads];
//Launch a group of threads
for (int i = 0; i < num_threads; ++i) {
td[i].thread_id = i;
pthread_create(&t[i], NULL, call_from_thread, (void *) &td[i]);
}
std::cout << "Launched from the main\n";
//Join the threads with the main thread
for (int i = 0; i < num_threads; ++i) {
pthread_join(t[i], NULL);
}
return 0;
}
$ g++ posix_thread_02.cpp -o posix_thread_02 -lpthread
$ ./posix_thread_02
Launched by thread 0
Launched from the main
Launched by thread 1
Launched by thread 2
Launched by thread 4
Launched by thread 3
再一次驗證了執行緒之間是競爭無序的。
二、C++ threads多執行緒實踐
C++11增加了多執行緒支援,能夠更好地在多核計算機上發揮性能。
一個簡單執行緒的實現
編譯時記得新增-std=c++11
選項
//Create a C++11 thread from the main program
#include <iostream>
#include <thread>
//This function will be called from a thread
void call_from_thread() {
std::cout << "Hello, World!" << std::endl;
}
int main() {
//Launch a thread
std::thread t1(call_from_thread);
//Join the thread with the main thread
t1.join();
return 0;
}
//lambda格式
auto func = [] (string name) {
cout << "hello" << name << endl;
};
thread t(func,"tom");
t.join();
我們可以和上文使用POSIX執行緒API建立的執行緒比較,本質是一樣的。
main函式建立了執行緒call_from_thread
,並在t1.join()
處完成。如果你忘記等待,main執行緒有可能先完成,此時程式退出時可能會殺死之前所建立的執行緒,而不管子執行緒是否已完成。
多個執行緒的實現
//Create a group of C++11 threads from the main program
#include <iostream>
#include <thread>
static const int num_threads = 10;
//This function will be called from a thread
void call_from_thread() {
std::cout << "Launched by thread\n";
}
int main() {
std::thread t[num_threads];
//Launch a group of threads
for (int i = 0; i < num_threads; ++i) {
t[i] = std::thread(call_from_thread);
}
std::cout << "Launched from the main\n";
//Join the threads with the main thread
for (int i = 0; i < num_threads; ++i) {
t[i].join();
}
return 0;
}
main函式也是一個執行緒,通常命名為主執行緒,因此上述程式碼實際運行了11個執行緒,我們也可以在主執行緒啟動子執行緒後做一些工作。 多個執行緒同時執行後沒有特定的順序,我們在程式設計時要確保執行緒不會修改臨界的資源。如果多個執行緒之間確實要競爭一些資源,我們需要編寫更復雜的並行程式碼,使用類似mutex、atomic等方法避免上述問題。
執行緒傳參的實現
#include <iostream>
#include <thread>
#include <string>
void thread_fun(std::string &str) {
std::cout << "thread fun";
std::cout << "msg is = " << str << std::endl;
str = "hello";
}
int main()
{
std::string str = "katty";
std::thread t(&thread_fun,std::ref(str));
std::cout<< "main thread = " << str << std::endl;
t.join();
return 0;
}
這裡使用的是Linux g++編譯器,使用了一些C++11新特性,大家根據自己的需要更改程式碼。 當然thread_fun也可以更改為lambdas 匿名函式實現。
四、類成員函式作為執行緒函式的實現
其實這次專案中真正遇到的是這個問題,如何將一個類成員函式作為一個執行緒函式實現呢?
很顯然,因為成員方法可以作為執行緒體來執行,所以獲得瞭如下好處:
- 執行緒的變數傳遞非常方便,直接讀寫成員變數即可;
- 執行緒體作為物件的一部分,可以訪問物件的私有變數和方法。
在參考了文章:成員函式作為pthread執行緒 之後,我選擇使用pthread和C++11 threads來實現之。
成員函式作為執行緒(POSIX threads)
我們再次複習下pthread的API介面:
int pthread_create(pthread_t *thread,pthread_attr_t *attr,void *(*routine)(void *),void *arg);
主要第三個引數routine是一個普通函式,而不能是一個類成員函式,這是為什麼呢? 這裡來測試下:
class A
{
public:
A(const char* name);
void sayHello();
void stop();
private:
const char* m_name ;
pthread_t m_thread;
};
A::A(const char*name)
{
m_name = name;
pthread_create(&m_thread,0,sayHello,this);
}
編譯報錯: 看到了吧:成員函式預設是非靜態函式,我們這樣做編譯失敗。 那麼我們能否嘗試將pthread_create的第三個引數改為靜態函式呢?
static void* createThread(void *arg)
{
A *pa = (A*)arg;
pa -> sayHello();
return (void*)0;
}
A::A(const char*name)
{
m_name = name;
pthread_create(&m_thread,0,createThread,this);
}
可以看到,我們通過static函式createThread
作為第三個引數,同時使用第四個引數this
執行緒傳參,進而在createThread
間接呼叫class中的成員方法,測試是可行的。
那我們能否有其他的辦法呢? 答案就是利用lambdas函式。
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
class A
{
public:
A(const char* name);
void sayHello();
void stop();
private:
const char* m_name ;
pthread_t m_thread;
};
A::A(const char*name)
{
m_name = name;
//call class method
auto func = [] (void* arg)
{
printf("work thread\n");
A* a = (A*)arg;
a->sayHello();
return (void*)0;
};
pthread_create(&m_thread,0,func,this);
}
void A::sayHello()
{
printf("helo,%s\n",m_name);
}
void A::stop()
{
pthread_join(m_thread,0);
}
int main()
{
A a("paopao");
printf("main thread\n");
a.stop();
return 0;
}
不過一定要記得:這兩個執行緒的執行順序並不固定!
成員函式作為執行緒(C++11 threads)
既然都寫到這裡了,那就把C++11的API學習下吧:
//#include <pthread.h>
#include <iostream>
#include <thread>
#include <string>
using namespace std;
class A
{
public:
A(string& name);
void sayHello();
void stop();
private:
string m_name;
thread* m_thread;
};
A::A(string& name)
{
m_name = name;
//call class method
auto func = [] (void* arg)
{
std::cout << "work thread\n";
A* a = (A*)arg;
a->sayHello();
return (void*)0;
};
m_thread = new thread(func,this);
}
void A::sayHello()
{
std::cout << "hello," << m_name <<std::endl;
}
void A::stop()
{
m_thread->join();
}
int main()
{
string str = "paopao";
A a(str);
std::cout << "main thread\n";
a.stop();
return 0;
}
//
//g++ -std=c++11 thread_03.cpp -o thread_03 -lpthread
本篇文章大概也就是這樣了,歡迎和大家一起探討~