欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++?多線程編程pthread的基本使用詳解

 更新時(shí)間:2024年05月30日 14:19:48   作者:Immortalqx  
在C++開(kāi)發(fā)中,原生的線程庫(kù)主要有兩個(gè),一個(gè)是C++11提供的<thread>(std::thread類),另一個(gè)是Linux下的<pthread.h>(p_thread類),本文主要介紹pthread的基本使用方式,需要的朋友可以參考下

在C++開(kāi)發(fā)中,原生的線程庫(kù)主要有兩個(gè),一個(gè)是C++11提供的<thread>(std::thread類),另一個(gè)是Linux下的<pthread.h>(p_thread類),本文主要介紹pthread的基本使用方式,線程基礎(chǔ)知識(shí)和std::thread的使用在上一篇博客中已經(jīng)有過(guò)介紹。

pthread簡(jiǎn)介

pthread中的p是POSIX的縮寫,而POSIX是Portable Operating System Interface的縮寫,是IEEE為要在各種UNIX操作系統(tǒng)上運(yùn)行軟件,而定義API的一系列互相關(guān)聯(lián)的標(biāo)準(zhǔn)的總稱。(Windows環(huán)境下無(wú)pthread,Linux GCC4.6以下編譯需加-pthread編譯選項(xiàng))

與std::thread對(duì)比:

std::thread是C++11中的新特性,將多線程程序的編寫提升到了語(yǔ)言層面,使得編寫的多線程程序的可移植性大大提高。

對(duì)二者特性對(duì)比,用一句話簡(jiǎn)單概括就是:std::thread更加簡(jiǎn)便易用,而pthread功能更加強(qiáng)大。

線程的創(chuàng)建和管理

每個(gè)線程都有一個(gè)在進(jìn)程中唯一的線程標(biāo)識(shí)符,用一個(gè)數(shù)據(jù)類型 pthread_t 表示,該數(shù)據(jù)類型在 Linux 中就是一個(gè)無(wú)符號(hào)長(zhǎng)整型數(shù)據(jù)。

創(chuàng)建新的線程

int pthread_create (pthread_t *thread,pthread_attr_t *attr,void *(*start_routine)(void *),void *arg);

若創(chuàng)建成功,返回0;若出錯(cuò),則返回錯(cuò)誤編號(hào)。

  • thread 是線程標(biāo)識(shí)符,但這個(gè)參數(shù)不是由用戶指定的,而是由 pthread_create 函數(shù)在創(chuàng)建時(shí)將新的線程的標(biāo)識(shí)符放到這個(gè)變量中。
  • attr 指定線程的屬性,可以用 NULL 表示默認(rèn)屬性。
  • start_routine 指定線程開(kāi)始運(yùn)行的函數(shù)。
  • arg 是 start_routine 所需要的參數(shù),是一個(gè)無(wú)類型指針。

默認(rèn)地,線程在被創(chuàng)建時(shí)要被賦予一定的屬性,這個(gè)屬性存放在數(shù)據(jù)類型 pthread_attr_t 中,它包含了線程的調(diào)度策略,堆棧的相關(guān)信息,join or detach 的狀態(tài)等。

pthread_attr_init 和 pthread_attr_destroy 函數(shù)分別用來(lái)創(chuàng)建和銷毀 pthread_attr_t,具體函數(shù)聲明可參考man幫助。

結(jié)束線程

當(dāng)發(fā)生以下情形之一時(shí),線程就會(huì)結(jié)束:

  • 線程運(yùn)行的函數(shù)return了,也就是線程的任務(wù)已經(jīng)完成;
  • 線程調(diào)用了 pthread_exit 函數(shù);
  • 其他線程調(diào)用 pthread_cancel 結(jié)束這個(gè)線程;
  • 進(jìn)程調(diào)用 exec() 或 exit(),結(jié)束了;
  • main() 函數(shù)先結(jié)束了,而且 main() 自己沒(méi)有調(diào)用 pthread_exit 來(lái)等所有線程完成任務(wù)。

當(dāng)然,一個(gè)線程結(jié)束,并不意味著它的所有信息都已經(jīng)消失,后面會(huì)看到僵尸線程的問(wèn)題。

下面介紹這兩個(gè)函數(shù)。

void pthread_exit (void *retval);

retval 是由用戶指定的參數(shù), pthread_exit 完成之后可以通過(guò)這個(gè)參數(shù)獲得線程的退出狀態(tài)。

int pthread_cancel (pthread_t thread);

一個(gè)線程可以通過(guò)調(diào)用 pthread_cancel 函數(shù)來(lái)請(qǐng)求取消同一進(jìn)程中的線程,這個(gè)線程由thread 參數(shù)指定。

如果操作成功則返回0,失敗則返回對(duì)應(yīng)的錯(cuò)誤編號(hào)。

一個(gè)簡(jiǎn)單的多線程實(shí)現(xiàn)

這里是一個(gè)非常簡(jiǎn)單的基于pthread的多線程實(shí)現(xiàn):

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_THREADS    5
void *PrintHello(void *threadid)
{
    long tid;
    tid = (long) threadid;
    printf("Hello World! It's me, thread #%ld!\n", tid);
    pthread_exit(NULL);
}
int main(int argc, char *argv[])
{
    pthread_t threads[NUM_THREADS];
    int rc;
    long t;
    for (t = 0; t < NUM_THREADS; t++)
    {
        printf("In main: creating thread %ld\n", t);
        rc = pthread_create(&threads[t], NULL, PrintHello, (void *) t);
        if (rc)
        {
            printf("ERROR; return code from pthread_create() is %d\n", rc);
            exit(-1);
        }
    }
    /* Last thing that main() should do */
    pthread_exit(NULL);
}

可以在終端通過(guò)下面的命令編譯執(zhí)行:

gcc -Wall hello.c -lpthread -o hello
./hello

輸出的結(jié)果為:

In main: creating thread 0
In main: creating thread 1
Hello World! It's me, thread #0!
In main: creating thread 2
Hello World! It's me, thread #1!
In main: creating thread 3
Hello World! It's me, thread #2!
In main: creating thread 4
Hello World! It's me, thread #3!
Hello World! It's me, thread #4!

注意輸出的順序可能不同, 要特別注意的是, main() 顯示地調(diào)用了 pthread_exit() 來(lái)等待其他線程的結(jié)束。(如果不使用這個(gè)函數(shù)的話,可能main()函數(shù)結(jié)束了也有線程沒(méi)有執(zhí)行完畢?。?/p>

給線程傳入初始化參數(shù)

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_THREADS    8
char *messages[NUM_THREADS];
struct thread_data
{
    int thread_id;
    int sum;
    char *message;
};
struct thread_data thread_data_array[NUM_THREADS];
void *PrintHello(void *threadarg)
{
    int taskid, sum;
    char *hello_msg;
    struct thread_data *my_data;
    sleep(1);
    my_data = (struct thread_data *) threadarg;
    taskid = my_data->thread_id;
    sum = my_data->sum;
    hello_msg = my_data->message;
    printf("Thread %d: %s  Sum=%d\n", taskid, hello_msg, sum);
    pthread_exit(NULL);
}
int main(int argc, char *argv[])
{
    pthread_t threads[NUM_THREADS];
    int *taskids[NUM_THREADS];
    int rc, t, sum;
    sum = 0;
    messages[0] = "English: Hello World!";
    messages[1] = "French: Bonjour, le monde!";
    messages[2] = "Spanish: Hola al mundo";
    messages[3] = "Klingon: Nuq neH!";
    messages[4] = "German: Guten Tag, Welt!";
    messages[5] = "Russian: Zdravstvytye, mir!";
    messages[6] = "Japan: Sekai e konnichiwa!";
    messages[7] = "Latin: Orbis, te saluto!";
    for (t = 0; t < NUM_THREADS; t++)
    {
        sum = sum + t;
        thread_data_array[t].thread_id = t;
        thread_data_array[t].sum = sum;
        thread_data_array[t].message = messages[t];
        printf("Creating thread %d\n", t);
        rc = pthread_create(&threads[t], NULL, PrintHello, (void *)
                &thread_data_array[t]);
        if (rc)
        {
            printf("ERROR; return code from pthread_create() is %d\n", rc);
            exit(-1);
        }
    }
    pthread_exit(NULL);
}

輸出結(jié)果:

Creating thread 0
Creating thread 1
Creating thread 2
Creating thread 3
Creating thread 4
Creating thread 5
Creating thread 6
Creating thread 7
Thread 4: German: Guten Tag, Welt!  Sum=10
Thread 5: Russian: Zdravstvytye, mir!  Sum=15
Thread 7: Latin: Orbis, te saluto!  Sum=28
Thread 2: Spanish: Hola al mundo  Sum=3
Thread 6: Japan: Sekai e konnichiwa!  Sum=21
Thread 3: Klingon: Nuq neH!  Sum=6
Thread 0: English: Hello World!  Sum=0
Thread 1: French: Bonjour, le monde!  Sum=1

對(duì)線程的阻塞(Joining and Detaching Threads)

阻塞是線程之間同步的一種方法。

int pthread_join(pthread_t threadid, void **value_ptr);

pthread_join 函數(shù)會(huì)讓調(diào)用它的線程等待 threadid 線程運(yùn)行結(jié)束之后再運(yùn)行。

value_ptr 存放了其他線程的返回值。

一個(gè)可以被join的線程,僅僅可以被別的一個(gè)線程 join,如果同時(shí)有多個(gè)線程嘗試 join 同一個(gè)線程時(shí),最終結(jié)果是未知的。另外,線程不能 join 自己。

上面提到過(guò),創(chuàng)建一個(gè)線程時(shí),要賦予它一定的屬性,這其中就包括joinable or detachable 的屬性,只有被聲明成joinable的線程,可以被其他線程join。

POSIX標(biāo)準(zhǔn)的最終版本指出線程應(yīng)該被設(shè)置成joinable的。顯式地設(shè)置一個(gè)線程為joinable,需要以下四個(gè)步驟:

  • Declare a pthread attribute variable of the pthread_attr_t data type
  • Initialize the attribute variable with pthread_attr_init()
  • Set the attribute detached status with pthread_attr_setdetachstate()
  • When done, free library resources used by the attribute with pthread_attr_destroy()

值得注意的的是:僵尸線程 ( “zombie” thread )是一種已經(jīng)退出了的 joinable 的線程,但是等待其他線程調(diào)用 pthread_join 來(lái) join 它,以收集它的退出信息(exit status)。如果沒(méi)有其他線程調(diào)用 pthread_join 來(lái) join 它的話,它占用的一些系統(tǒng)資源不會(huì)被釋放,比如堆棧。如果main()函數(shù)需要長(zhǎng)時(shí)間運(yùn)行,并且創(chuàng)建大量 joinable 的線程,就有可能出現(xiàn)堆棧不足的 error 。

對(duì)于那些不需要 join 的線程,最好利用 pthread_detach,這樣它運(yùn)行結(jié)束后,資源就會(huì)及時(shí)得到釋放。注意一個(gè)線程被使用 pthread_detach 之后,它就不能再被改成 joinable 的了。

總而言之,創(chuàng)建的每一個(gè)線程都應(yīng)該使用 pthread_join 或者 pthread_detach 其中一個(gè),以防止僵尸線程的出現(xiàn)。

相關(guān)函數(shù):

pthread_detach (threadid)
pthread_attr_setdetachstate (attr,detachstate)
pthread_attr_getdetachstate (attr,detachstate)

顯式地設(shè)置一個(gè)線程為 joinable

#include<stdlib.h>
#include <pthread.h>
#include <stdio.h>
#include <math.h>
#define NUM_THREADS 4
void *BusyWork(void *t)
{
    int i;
    long tid;
    double result = 0.0;
    tid = (long) t;
    printf("Thread %ld starting...\n", tid);
    for (i = 0; i < 1000000; i++)
    {
        result = result + sin(i) * tan(i);
    }
    printf("Thread %ld done. Result = %e\n", tid, result);
    pthread_exit((void *) t);
}
int main(int argc, char *argv[])
{
    pthread_t thread[NUM_THREADS];
    pthread_attr_t attr;
    int rc;
    long t;
    void *status;
/* Initialize and set thread detached attribute */
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    for (t = 0; t < NUM_THREADS; t++)
    {
        printf("Main: creating thread %ld\n", t);
        rc = pthread_create(&thread[t], &attr, BusyWork, (void *) t);
        if (rc)
        {
            printf("ERROR; return code from pthread_create() is %d\n", rc);
            exit(-1);
        }
    }
/* Free attribute and wait for the other threads */
    pthread_attr_destroy(&attr);
    for (t = 0; t < NUM_THREADS; t++)
    {
        rc = pthread_join(thread[t], &status);
        if (rc)
        {
            printf("ERROR; return code from pthread_join() is %d\n", rc);
            exit(-1);
        }
        printf("Main: completed join with thread %ld having a status of %ld\n", t, (long) status);
    }
    printf("Main: program completed. Exiting.\n");
    pthread_exit(NULL);
}

輸出結(jié)果:

Main: creating thread 0
Main: creating thread 1
Thread 0 starting...
Thread 1 starting...
Thread 0 done. Result = 3.135632e+06
Main: completed join with thread 0 having a status of 0
Thread 1 done. Result = 3.135632e+06
Main: completed join with thread 1 having a status of 1
Main: program completed. Exiting.

堆棧管理 (Stack Management)

相關(guān)函數(shù):

pthread_attr_getstacksize (attr, stacksize)
pthread_attr_setstacksize (attr, stacksize)
pthread_attr_getstackaddr (attr, stackaddr)
pthread_attr_setstackaddr (attr, stackaddr)

POSIX標(biāo)準(zhǔn)沒(méi)有規(guī)定一個(gè)線程的堆棧大小。安全可移植的程序不會(huì)依賴于具體實(shí)現(xiàn)默認(rèn)的堆棧限制,而是顯式地調(diào)用 pthread_attr_setstacksize 來(lái)分配足夠的堆棧空間。

關(guān)于堆棧大小的一個(gè)例子

#include<stdlib.h>
#include <pthread.h>
#include <stdio.h>
#define NTHREADS 4
#define N 1000
#define MEGEXTRA 1000000
pthread_attr_t attr;
void *dowork(void *threadid)
{
    double A[N][N];
    int i, j;
    long tid;
    size_t mystacksize;
    tid = (long) threadid;
    pthread_attr_getstacksize(&attr, &mystacksize);
    printf("Thread %ld: stack size = %li bytes \n", tid, mystacksize);
    for (i = 0; i < N; i++)
        for (j = 0; j < N; j++)
            A[i][j] = ((i * j) / 3.452) + (N - i);
    pthread_exit(NULL);
}
int main(int argc, char *argv[])
{
    pthread_t threads[NTHREADS];
    size_t stacksize;
    int rc;
    long t;
    pthread_attr_init(&attr);
    pthread_attr_getstacksize(&attr, &stacksize);
    printf("Default stack size = %li\n", stacksize);
    stacksize = sizeof(double) * N * N + MEGEXTRA;
    printf("Amount of stack needed per thread = %li\n", stacksize);
    pthread_attr_setstacksize(&attr, stacksize);
    printf("Creating threads with stack size = %li bytes\n", stacksize);
    for (t = 0; t < NTHREADS; t++)
    {
        rc = pthread_create(&threads[t], &attr, dowork, (void *) t);
        if (rc)
        {
            printf("ERROR; return code from pthread_create() is %d\n", rc);
            exit(-1);
        }
    }
    printf("Created %ld threads.\n", t);
    pthread_exit(NULL);
}

輸出結(jié)果:

Default stack size = 8388608
Amount of stack needed per thread = 9000000
Creating threads with stack size = 9000000 bytes
Thread 2: stack size = 9000000 bytes 
Created 4 threads.
Thread 1: stack size = 9000000 bytes 
Thread 0: stack size = 9000000 bytes 
Thread 3: stack size = 9000000 bytes 

其他重要函數(shù)

pthread_self ();
pthread_equal (thread1,thread2);

調(diào)用pthread_self 可以返回它的 thread ID。

pthread_equal 比較兩個(gè)線程的 ID,如果不同則返回0,否則返回一個(gè)非零值。

互斥鎖 Mutex

Mutex 常常被用來(lái)保護(hù)那些可以被多個(gè)線程訪問(wèn)的共享資源,比如可以防止多個(gè)線程同時(shí)更新同一個(gè)數(shù)據(jù)時(shí)出現(xiàn)混亂。

使用互斥鎖的一般步驟是:

  • 創(chuàng)建一個(gè)互斥鎖,即聲明一個(gè)pthread_mutex_t類型的數(shù)據(jù),然后初始化,只有初始化之后才能使用;
  • 多個(gè)線程嘗試鎖定這個(gè)互斥鎖;
  • 只有一個(gè)成功鎖定互斥鎖,成為互斥鎖的擁有者,然后進(jìn)行一些指令;
  • 擁有者解鎖互斥鎖;
  • 其他線程嘗試鎖定這個(gè)互斥鎖,重復(fù)上面的過(guò)程;
  • 最后互斥鎖被顯式地調(diào)用 pthread_mutex_destroy 來(lái)進(jìn)行銷毀。

有兩種方式初始化一個(gè)互斥鎖:

第一種,利用已經(jīng)定義的常量初始化,例如

pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER;

第二種方式是調(diào)用 pthread_mutex_init (mutex,attr) 進(jìn)行初始化。

當(dāng)多個(gè)線程同時(shí)去鎖定同一個(gè)互斥鎖時(shí),失敗的那些線程,如果是用 pthread_mutex_lock 函數(shù),那么會(huì)被阻塞,直到這個(gè)互斥鎖被解鎖,它們?cè)倮^續(xù)競(jìng)爭(zhēng);如果是用 pthread_mutex_trylock 函數(shù),那么失敗者只會(huì)返回一個(gè)錯(cuò)誤。

最后需要指出的是,保護(hù)共享數(shù)據(jù)是程序員的責(zé)任。程序員要負(fù)責(zé)所有可以訪問(wèn)該數(shù)據(jù)的線程都使用mutex這種機(jī)制,否則,不使用 mutex 的線程還是有可能對(duì)數(shù)據(jù)造成破壞。

相關(guān)函數(shù) (具體聲明可以用 man 查看 )

pthread_mutex_init (mutex,attr);
pthread_mutex_destroy (pthread_mutex_t *mutex);
pthread_mutexattr_init (attr);
pthread_mutexattr_destroy (attr);
phtread_mutex_lock(pthread_mutex_t *mutex);
phtread_mutex_trylock(pthread_mutex_t *mutex);
phtread_mutex_unlock(pthread_mutex_t *mutex);

示例

下面是一個(gè)利用多線程進(jìn)行向量點(diǎn)乘的程序,其中需要對(duì) dotstr.sum 這個(gè)共同讀寫的數(shù)據(jù)進(jìn)行保護(hù)。

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
/*   
The following structure contains the necessary information  
to allow the function "dotprod" to access its input data and 
place its output into the structure.  This structure is 
unchanged from the sequential version.
*/
typedef struct
{
    double *a;
    double *b;
    double sum;
    int veclen;
} DOTDATA;
/* Define globally accessible variables and a mutex */
#define NUMTHRDS 4
#define VECLEN 100000
DOTDATA dotstr;
pthread_t callThd[NUMTHRDS];
pthread_mutex_t mutexsum;
/*
The function dotprod is activated when the thread is created.
As before, all input to this routine is obtained from a structure 
of type DOTDATA and all output from this function is written into
this structure. The benefit of this approach is apparent for the 
multi-threaded program: when a thread is created we pass a single
argument to the activated function - typically this argument
is a thread number. All  the other information required by the 
function is accessed from the globally accessible structure. 
*/
void *dotprod(void *arg)
{
/* Define and use local variables for convenience */
    int i, start, end, len;
    long offset;
    double mysum, *x, *y;
    offset = (long) arg;
    len = dotstr.veclen;
    start = offset * len;
    end = start + len;
    x = dotstr.a;
    y = dotstr.b;
/*
Perform the dot product and assign result
to the appropriate variable in the structure. 
*/
    mysum = 0;
    for (i = start; i < end; i++)
    {
        mysum += (x[i] * y[i]);
    }
/*
Lock a mutex prior to updating the value in the shared
structure, and unlock it upon updating.
*/
    pthread_mutex_lock(&mutexsum);
    dotstr.sum += mysum;
    printf("Thread %ld did %d to %d: mysum=%f global sum=%f\n", offset, start,
           end, mysum, dotstr.sum);
    pthread_mutex_unlock(&mutexsum);
    pthread_exit((void *) 0);
}
/* 
The main program creates threads which do all the work and then print out result 
upon completion. Before creating the threads, The input data is created. Since 
all threads update a shared structure, we need a mutex for mutual exclusion. 
The main thread needs to wait for all threads to complete, it waits for each one 
of the threads. We specify a thread attribute value that allow the main thread to
join with the threads it creates. Note also that we free up handles  when they
are no longer needed.
*/
int main(int argc, char *argv[])
{
    long i;
    double *a, *b;
    void *status;
    pthread_attr_t attr;
/* Assign storage and initialize values */
    a = (double *) malloc(NUMTHRDS * VECLEN * sizeof(double));
    b = (double *) malloc(NUMTHRDS * VECLEN * sizeof(double));
    for (i = 0; i < VECLEN * NUMTHRDS; i++)
    {
        a[i] = 1;
        b[i] = a[i];
    }
    dotstr.veclen = VECLEN;
    dotstr.a = a;
    dotstr.b = b;
    dotstr.sum = 0;
    pthread_mutex_init(&mutexsum, NULL);
/* Create threads to perform the dotproduct  */
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    for (i = 0; i < NUMTHRDS; i++)
    {
        /* Each thread works on a different set of data.
         * The offset is specified by 'i'. The size of
         * the data for each thread is indicated by VECLEN.
         */
        pthread_create(&callThd[i], &attr, dotprod, (void *) i);
    }
    pthread_attr_destroy(&attr);
/* Wait on the other threads */
    for (i = 0; i < NUMTHRDS; i++)
    {
        pthread_join(callThd[i], &status);
    }
/* After joining, print out the results and cleanup */
    printf("Sum =  %f \n", dotstr.sum);
    free(a);
    free(b);
    pthread_mutex_destroy(&mutexsum);
    pthread_exit(NULL);
}

輸出結(jié)果:

Thread 0 did 0 to 100000:  mysum=100000.000000 global sum=100000.000000
Thread 1 did 100000 to 200000:  mysum=100000.000000 global sum=200000.000000
Thread 2 did 200000 to 300000:  mysum=100000.000000 global sum=300000.000000
Thread 3 did 300000 to 400000:  mysum=100000.000000 global sum=400000.000000
Sum =  400000.000000 

條件變量 Condition Variable

互斥鎖只有兩種狀態(tài),這限制了它的用途. 條件變量允許線程在阻塞的時(shí)候等待另一個(gè)線程發(fā)送的信號(hào),當(dāng)收到信號(hào)后,阻塞的線程就被喚醒并試圖鎖定與之相關(guān)的互斥鎖. 條件變量要和互斥鎖結(jié)合使用。

條件變量的聲明和初始化

通過(guò)聲明 pthread_cond_t 類型的數(shù)據(jù),并且必須先初始化才能使用。

初始化的方法也有兩種:

第一種,利用內(nèi)部定義的常量,例如:

pthread_cond_t myconvar = PTHREAD_COND_INITIALIZER;

第二種,利用函數(shù) pthread_cond_init(cond,attr),其中 attr 由 pthread_condattr_init() 和 pthread_condattr_destroy() 創(chuàng)建和銷毀。

可以用 pthread_cond_destroy() 銷毀一個(gè)條件變量。

相關(guān)函數(shù):

pthread_cond_wait (condition,mutex);
pthread_cond_signal (condition);
pthread_cond_broadcast (condition);

pthread_cond_wait() 會(huì)阻塞調(diào)用它的線程,直到收到某一信號(hào)。這個(gè)函數(shù)需要在 mutex 已經(jīng)被鎖之后進(jìn)行調(diào)用,并且當(dāng)線程被阻塞時(shí),會(huì)自動(dòng)解鎖 mutex。信號(hào)收到后,線程被喚醒,這時(shí) mutex 又會(huì)被這個(gè)線程鎖定。

pthread_cond_signal() 函數(shù)結(jié)束時(shí),必須解鎖 mutex,以供 pthread_cond_wait() 鎖定mutex。

當(dāng)不止一個(gè)線程在等待信號(hào)時(shí),要用 pthread_cond_broadcast() 代替 pthread_cond_signal() 來(lái)告訴所有被該條件變量阻塞的線程結(jié)束阻塞狀態(tài)。

示例

下面是一個(gè)例子,三個(gè)線程共同訪問(wèn) count 變量,thread 2 和 thread 3 競(jìng)爭(zhēng)地對(duì)其進(jìn)行加 1 的操作,thread 1 等 count 達(dá)到 12 的時(shí)候,一次性加 125 。 然后 thread 2 和 thread 3 再去競(jìng)爭(zhēng) count 的控制權(quán),直到完成自己的對(duì) count 加 10 次的任務(wù)。

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_THREADS  3
#define TCOUNT 10
#define COUNT_LIMIT 12
int count = 0;
pthread_mutex_t count_mutex;
pthread_cond_t count_threshold_cv;
void *inc_count(void *t)
{
    int i;
    long my_id = (long) t;
    for (i = 0; i < TCOUNT; i++)
    {
        pthread_mutex_lock(&count_mutex);
        count++;
        /* 
        Check the value of count and signal waiting thread when condition is
        reached.  Note that this occurs while mutex is locked. 
        */
        if (count == COUNT_LIMIT)
        {
            printf("inc_count(): thread %ld, count = %d  Threshold reached. ",
                   my_id, count);
            pthread_cond_signal(&count_threshold_cv);
            printf("Just sent signal.\n");
        }
        printf("inc_count(): thread %ld, count = %d, unlocking mutex\n",
               my_id, count);
        pthread_mutex_unlock(&count_mutex);
        /* Do some work so threads can alternate on mutex lock */
        sleep(1);
    }
    pthread_exit(NULL);
}
void *watch_count(void *t)
{
    long my_id = (long) t;
    printf("Starting watch_count(): thread %ld\n", my_id);
    /*
    Lock mutex and wait for signal.  Note that the pthread_cond_wait routine
    will automatically and atomically unlock mutex while it waits. 
    Also, note that if COUNT_LIMIT is reached before this routine is run by
    the waiting thread, the loop will be skipped to prevent pthread_cond_wait
    from never returning.
    */
    pthread_mutex_lock(&count_mutex);
    while (count < COUNT_LIMIT)
    {
        printf("watch_count(): thread %ld Count= %d. Going into wait...\n", my_id, count);
        pthread_cond_wait(&count_threshold_cv, &count_mutex);
        printf("watch_count(): thread %ld Condition signal received. Count= %d\n", my_id,
               count);
        printf("watch_count(): thread %ld Updating the value of count...\n", my_id, count);
        count += 125;
        printf("watch_count(): thread %ld count now = %d.\n", my_id, count);
    }
    printf("watch_count(): thread %ld Unlocking mutex.\n", my_id);
    pthread_mutex_unlock(&count_mutex);
    pthread_exit(NULL);
}
int main(int argc, char *argv[])
{
    int i, rc;
    long t1 = 1, t2 = 2, t3 = 3;
    pthread_t threads[3];
    pthread_attr_t attr;
    /* Initialize mutex and condition variable objects */
    pthread_mutex_init(&count_mutex, NULL);
    pthread_cond_init(&count_threshold_cv, NULL);
    /* For portability, explicitly create threads in a joinable state */
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    pthread_create(&threads[0], &attr, watch_count, (void *) t1);
    pthread_create(&threads[1], &attr, inc_count, (void *) t2);
    pthread_create(&threads[2], &attr, inc_count, (void *) t3);
    /* Wait for all threads to complete */
    for (i = 0; i < NUM_THREADS; i++)
    {
        pthread_join(threads[i], NULL);
    }
    printf("Main(): Waited and joined with %d threads. Final value of count = %d. Done.\n",
           NUM_THREADS, count);
    /* Clean up and exit */
    pthread_attr_destroy(&attr);
    pthread_mutex_destroy(&count_mutex);
    pthread_cond_destroy(&count_threshold_cv);
    pthread_exit(NULL);
}

輸出結(jié)果:

inc_count(): thread 3, count = 1, unlocking mutex
inc_count(): thread 2, count = 2, unlocking mutex
Starting watch_count(): thread 1
watch_count(): thread 1 Count= 2. Going into wait...
inc_count(): thread 3, count = 3, unlocking mutex
inc_count(): thread 2, count = 4, unlocking mutex
inc_count(): thread 3, count = 5, unlocking mutex
inc_count(): thread 2, count = 6, unlocking mutex
inc_count(): thread 3, count = 7, unlocking mutex
inc_count(): thread 2, count = 8, unlocking mutex
inc_count(): thread 3, count = 9, unlocking mutex
inc_count(): thread 2, count = 10, unlocking mutex
inc_count(): thread 3, count = 11, unlocking mutex
inc_count(): thread 2, count = 12  Threshold reached. Just sent signal.
inc_count(): thread 2, count = 12, unlocking mutex
watch_count(): thread 1 Condition signal received. Count= 12
watch_count(): thread 1 Updating the value of count...
watch_count(): thread 1 count now = 137.
watch_count(): thread 1 Unlocking mutex.
inc_count(): thread 3, count = 138, unlocking mutex
inc_count(): thread 2, count = 139, unlocking mutex
inc_count(): thread 3, count = 140, unlocking mutex
inc_count(): thread 2, count = 141, unlocking mutex
inc_count(): thread 3, count = 142, unlocking mutex
inc_count(): thread 2, count = 143, unlocking mutex
inc_count(): thread 3, count = 144, unlocking mutex
inc_count(): thread 2, count = 145, unlocking mutex
Main(): Waited and joined with 3 threads. Final value of count = 145. Done.

到此這篇關(guān)于C++ 多線程編程pthread的基本使用詳解的文章就介紹到這了,更多相關(guān)C++ pthread使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C++?NFS掛載及掛載命令

    C++?NFS掛載及掛載命令

    這篇文章主要介紹了C++?NFS掛載,文中給大家提到了掛載NFS時(shí)常用的命令,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-12-12
  • C++的輸入與輸出和格式化輸出

    C++的輸入與輸出和格式化輸出

    這篇文章主要介紹了詳解C++中的輸入與輸出和格式化輸出,是C++入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考,希望能夠給你帶來(lái)幫助
    2021-11-11
  • C語(yǔ)言 函數(shù)指針(指向函數(shù)的指針)詳解

    C語(yǔ)言 函數(shù)指針(指向函數(shù)的指針)詳解

    本文主要介紹 C語(yǔ)言函數(shù)指針的知識(shí),這里整理了詳細(xì)的資料及示例代碼以便大家學(xué)習(xí)參考,有需要學(xué)習(xí)此部分知識(shí)的朋友可以參考下
    2016-08-08
  • C++迭代器和顯示類型轉(zhuǎn)換方式

    C++迭代器和顯示類型轉(zhuǎn)換方式

    這篇文章主要介紹了C++迭代器和顯示類型轉(zhuǎn)換方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • C++實(shí)現(xiàn)自底向上的歸并排序算法

    C++實(shí)現(xiàn)自底向上的歸并排序算法

    這篇文章主要介紹了C++實(shí)現(xiàn)自底向上的歸并排序算法,結(jié)合實(shí)例形式較為詳細(xì)的分析總結(jié)了自底向上的歸并排序算法的原理與具體實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2015-12-12
  • 在vscode中快速新建html文件的2種方法總結(jié)

    在vscode中快速新建html文件的2種方法總結(jié)

    這篇文章主要給大家介紹了關(guān)于在vscode中快速新建html文件的2種方法,以及如何快速打開(kāi)HTML文件查看編輯效果的方法,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2022-04-04
  • C語(yǔ)言素?cái)?shù)(質(zhì)數(shù))判斷的3種方法舉例

    C語(yǔ)言素?cái)?shù)(質(zhì)數(shù))判斷的3種方法舉例

    這篇文章主要給大家介紹了關(guān)于C語(yǔ)言素?cái)?shù)(質(zhì)數(shù))判斷的3種方法,質(zhì)數(shù)是只能被1或者自身整除的自然數(shù)(不包括1),稱為質(zhì)數(shù),文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-11-11
  • C 語(yǔ)言基礎(chǔ)教程(我的C之旅開(kāi)始了)[三]

    C 語(yǔ)言基礎(chǔ)教程(我的C之旅開(kāi)始了)[三]

    C 語(yǔ)言基礎(chǔ)教程(我的C之旅開(kāi)始了)[三]...
    2007-02-02
  • C++課程設(shè)計(jì)之運(yùn)動(dòng)會(huì)管理系統(tǒng)

    C++課程設(shè)計(jì)之運(yùn)動(dòng)會(huì)管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C++課程設(shè)計(jì)之運(yùn)動(dòng)會(huì)管理系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-10-10
  • Qt中CQGUI框架之陰影圓角窗口實(shí)現(xiàn)

    Qt中CQGUI框架之陰影圓角窗口實(shí)現(xiàn)

    這篇文章主要介紹了Qt中CQGUI框架之陰影圓角窗口實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03

最新評(píng)論