1. 程式人生 > >LInux C++多執行緒程式設計基礎(彙總)

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