LInux C++多執行緒程式設計基礎(彙總)
1. 前言
本次來寫一篇關於C++多執行緒的基本使用。前面有一篇是互斥鎖的入門,學了兩天,做一下總結。
2. 多執行緒
(1) 建立
多執行緒的表示pthread_t:
/* Thread identifiers. The structure of the attribute type is not
exposed on purpose. */
typedef unsigned long int pthread_t;
在原始碼中,pthread_t實際上就只是一個無符號的long int變數,用於執行緒的唯一識別符號。
下面是pthread_create的原型:
/* Create a new thread, starting with execution of START-ROUTINE getting passed ARG. Creation attributed come from ATTR. The new handle is stored in *NEWTHREAD. */ extern int pthread_create (pthread_t *__restrict __newthread, const pthread_attr_t *__restrict __attr, void *(*__start_routine) (void *), void *__restrict __arg) __THROWNL __nonnull ((1, 3));
引數介紹:
__newthread表示執行緒的唯一識別符號
__attr表示執行緒的屬性,之後會介紹幾個小方面的
__*(*__start_rountine)(void *)這個實際上就是一個函式的指標
__arg表示函式的引數,需要將其轉化成void*指標才可以傳值
執行緒建立之後如果不進行其他的操作,那麼執行緒會在主執行緒結束的時候就結束,主執行緒不會等到所有執行緒結束後才結束,例如下面的程式碼:
#include <iostream> #include <pthread.h> #include <stdio.h> #include <semaphore.h> #include <stdlib.h> #include <string.h> using namespace std; pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t count_nonzero; unsigned count; void* decrement_count(void* data) { pthread_mutex_lock(&mut); while(count == 0) { printf("1\n"); pthread_cond_wait(&count_nonzero,&mut); } count = count -1; printf("Dec\n"); pthread_mutex_unlock(&mut); } void* increment_count(void* data) { pthread_mutex_lock(&mut); if(count == 0) pthread_cond_signal(&count_nonzero); count = count + 1; printf("inc\n"); pthread_mutex_unlock(&mut); } int main() { count = 0; pthread_t tid[2]; pthread_create(&tid[0],NULL,decrement_count,NULL); pthread_create(&tid[1],NULL,increment_count,NULL); printf("main\n"); }
其執行結果可以為下面的一種:
第一種
1
main
第二種
inc
main
第三種
1
inc
main
...
當然後面那些需要你的執行緒搶到主執行緒的資源,否則你會發現永遠是第一種情況。
第一種是什麼情況呢?
很簡單,主執行緒建立了新的執行緒,執行緒的執行順序是沒有保證的,這個時候第一個執行緒就先執行,但是又發現count = 0,執行緒就把鎖釋放。然後回到主執行緒,主執行緒又因為在main之後就結束了,使得這兩個執行緒根本就沒有時間執行,導致輸出以這種結果。
(2) 執行緒的屬性
屬性物件主要包括是否繫結,是否分離,堆疊地址,堆疊大小,優先順序等。
分離屬性(detachstate):
分離:執行緒自己執行結束,則馬上釋放資源。
不分離:如果加入pthread_join中,則需要等待pthread_join執行完畢之後才算結束,並且釋放資源。
所對應的巨集定義為:
分離:PTHREAD_CREATE_DETACHED
不分離:PTHREAD_CREATE_JOINABLE
設定函式為:
/* Set detach state attribute. */
extern int pthread_attr_setdetachstate (pthread_attr_t *__attr,
int __detachstate)
__THROW __nonnull ((1));
注意,如果設定執行緒為分離的時候,執行緒的可能回現在pthread_create函式之前已經完成,導致返回錯誤的執行緒識別符號。可以新增pthread_cond_timewait()來讓執行緒等待一段時間,不能使用wait(),這個會使得整個程序都進入等待,並不能解決問題。
優先順序屬性(SCHED_PARAM):
就是執行緒執行的優先順序,優先順序高的先執行,當然,這在併發程式設計中也是沒有保證的。
設定函式:
/* Return in *PARAM the scheduling parameters of *ATTR. */
extern int pthread_attr_getschedparam (const pthread_attr_t *__restrict __attr,
struct sched_param *__restrict __param)
__THROW __nonnull ((1, 2));
/* Set scheduling parameters (priority, etc) in *ATTR according to PARAM. */
extern int pthread_attr_setschedparam (pthread_attr_t *__restrict __attr,
const struct sched_param *__restrict
__param) __THROW __nonnull ((1, 2));
設定樣例:
pthread_t tid[2];
pthread_attr_t attr;
sched_param param;
pthread_attr_init(&attr);
pthread_attr_getschedparam(&attr,¶m);
param.__sched_priority = 20;
pthread_attr_setschedparam(&attr,¶m);
pthread_create(&tid[0],&attr,decrement_count,NULL);
(3) pthread_join/pthread_exit
/* Make calling thread wait for termination of the thread TH. The
exit status of the thread is stored in *THREAD_RETURN, if THREAD_RETURN
is not NULL.
This function is a cancellation point and therefore not marked with
__THROW. */
extern int pthread_join (pthread_t __th, void **__thread_return);
pthread_join:可以將不分離的執行緒連線起來,直到這個函式呼叫結束的時候,執行緒才釋放資源。這樣也就可以解決前面的出現那種主執行緒結束了,執行緒沒有執行完成就結束的情況。當然,這個會阻塞當前執行緒,直到所有的執行緒結束為止。
/* Terminate calling thread.
The registered cleanup handlers are called via exception handling
so we cannot mark this function with __THROW.*/
extern void pthread_exit (void *__retval) __attribute__ ((__noreturn__));
pthread_exit退出執行緒。這個後面的引數是返回值,返回值可以在__thread_return中。
下面是之前的程式碼:
#include <iostream>
#include <pthread.h>
#include <stdio.h>
#include <semaphore.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t count_nonzero;
unsigned count;
void* decrement_count(void* data)
{
pthread_mutex_lock(&mut);
while(count == 0)
{
printf("1\n");
pthread_cond_wait(&count_nonzero,&mut);
}
count = count -1;
printf("Dec\n");
pthread_mutex_unlock(&mut);
pthread_exit(NULL);
}
void* increment_count(void* data)
{
pthread_mutex_lock(&mut);
if(count == 0)
pthread_cond_signal(&count_nonzero);
count = count + 1;
printf("inc\n");
pthread_mutex_unlock(&mut);
pthread_exit(NULL);
}
int main()
{
count = 0;
pthread_t tid[2];
pthread_attr_t attr;
sched_param param;
pthread_attr_init(&attr);
pthread_attr_getschedparam(&attr,¶m);
param.__sched_priority = 20;
pthread_attr_setschedparam(&attr,¶m);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE);
pthread_create(&tid[0],&attr,decrement_count,NULL);
pthread_create(&tid[1],&attr,increment_count,NULL);
pthread_join(tid[0],NULL);
pthread_join(tid[1],NULL);
printf("main\n");
}
執行結果為:
1
inc
Dec
main
到這裡,執行緒的知識基本上就這些了。
3. C++11 thread類
C++11中新添加了一個執行緒的類,在<thread>標頭檔案中。
使用示例:
#include <iostream>
#include <pthread.h>
#include <stdio.h>
#include <semaphore.h>
#include <stdlib.h>
#include <string.h>
#include <thread>
using namespace std;
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t count_nonzero;
unsigned count;
void decrement_count()
{
pthread_mutex_lock(&mut);
while(count == 0)
{
printf("1\n");
pthread_cond_wait(&count_nonzero,&mut);
}
count = count -1;
printf("Dec\n");
pthread_mutex_unlock(&mut);
pthread_exit(NULL);
}
void increment_count()
{
pthread_mutex_lock(&mut);
if(count == 0)
pthread_cond_signal(&count_nonzero);
count = count + 1;
printf("inc\n");
pthread_mutex_unlock(&mut);
pthread_exit(NULL);
}
int main()
{
count = 0;
thread t[2];
t[0] = thread(decrement_count);
t[1] = thread(increment_count);
t[0].join();
t[1].join();
}
有沒有覺得函式變得簡單易懂,而且很符合我們的程式設計習慣了= =
這個需要在執行join的時候才會執行執行緒,如果在主執行緒結束之前,沒有join,會輸出錯誤。
而且這個join是不會保證執行的順序的,也就是主執行緒和join的執行緒的執行順序是沒有保證的,join只是保證join的執行緒一定能夠執行到結束。
參考連結:
https://blog.csdn.net/stone_overlooking/article/details/78520945
http://www.runoob.com/cplusplus/cpp-multithreading.html
https://blog.csdn.net/chenxun_2010/article/details/49785611