C語言多線程操做

C語言多線程

多線程是多任務處理的一種特殊形式,多任務處理容許讓電腦同時運行兩個或兩個以上的程序。通常狀況下,兩種類型的多任務處理:基於進程和基於線程html

  • 基於進程的多任務處理是程序的併發執行。
  • 基於線程的多任務處理是同一程序的片斷的併發執行。

多線程程序包含能夠同時運行的兩個或多個部分。這樣的程序中的每一個部分稱爲一個線程,每一個線程定義了一個單獨的執行路徑。編程

本教程假設您使用的是 Linux 操做系統,咱們要使用 POSIX 編寫多線程 C++ 程序。POSIX Threads 或 Pthreads 提供的 API 可在多種類 Unix POSIX 系統上可用,好比 FreeBSD、NetBSD、GNU/Linux、Mac OS X 和 Solaris。數組

建立線程

下面的程序,咱們能夠用它來建立一個POSIX 線程:多線程

#include <pthread.h>
pthread_create (thread, attr, start_routine, arg)

在這裏,pthread_create 建立一個新的線程,並讓它可執行。下面是關於參數的說明:併發

參數 描述
thread 指向線程標識符指針。
attr 一個不透明的屬性對象,能夠被用來設置線程屬性。您能夠指定線程屬性對象,也能夠使用默認值 NULL。
start_routine 線程運行函數起始地址,一旦線程被建立就會執行。
arg 運行函數的參數。它必須經過把引用做爲指針強制轉換爲 void 類型進行傳遞。若是沒有傳遞參數,則使用 NULL。

建立線程成功時,函數返回 0,若返回值不爲 0 則說明建立線程失敗。函數

終止線程

使用下面的程序,咱們能夠用它來終止一個 POSIX 線程:post

#include <pthread.h>
pthread_exit (status)

在這裏,pthread_exit 用於顯式地退出一個線程。一般狀況下,pthread_exit() 函數是在線程完成工做後無需繼續存在時被調用。url

若是 main() 是在它所建立的線程以前結束,並經過 pthread_exit() 退出,那麼其餘線程將繼續執行。不然,它們將在 main() 結束時自動被終止。spa

鏈接和分離線程

咱們能夠使用如下兩個函數來鏈接或分離線程:操作系統

pthread_join (threadid, status) 
pthread_detach (threadid)

pthread_join() 子程序阻礙調用程序,直到指定的 threadid 線程終止爲止。當建立一個線程時,它的某個屬性會定義它是不是可鏈接的(joinable)或可分離的(detached)。只有建立時定義爲可鏈接的線程才能夠被鏈接。若是線程建立時被定義爲可分離的,則它永遠也不能被連。pthread_join() 函數來等待線程的完成。

注意

pthread庫不是Linux系統默認的庫,鏈接時須要使用庫libpthread.a, 在使用pthread_create建立線程時,在編譯中要加-lpthread參數:

 

  gcc createThread.c -lpthread -o createThread.o
  ./createThread.

Test 1 無參數傳遞的線程併發編程實例

// 基於線程的併發編程
// Test_1 createThread
#include <stdio.h>
#include <pthread.h>
/*
 * pthread庫不是Linux系統默認的庫,鏈接時須要使用庫libpthread.a,
 * 在使用pthread_create建立線程時,在編譯中要加-lpthread參數:
 * gcc createThread.c -lpthread -o createThread.o
 * ./createThread.o
 * 加上上面兩句之後編譯成功,並輸出結果
 * */
#define NUM_Threads 5

// 線程的運行函數
void *PrintHello(void *arg)
{
    printf("Hello,World of Thread in C!\n");
    return 0;
}

int main()
{
    int i;
    int ret;
    // 定義線程的id變量,多個變量使用數組
    pthread_t tids[NUM_Threads];

    for (i=0; i<NUM_Threads; i++)
    {
        // 參數依次是: 建立的線程id,線程參數,調用的函數,傳入的函數參數
       ret = pthread_create(&tids[i], NULL, PrintHello, NULL);
       if (ret != 0)
      {
          printf("pthread_create error: error_code = \n");
      }
    }
    // 等各個線程推出後,進程才結束
    pthread_exit(NULL);

    return 0;
}

/*
 * 在CLion(Ubuntu)中輸出結果爲
Hello,World of Thread in C!
Hello,World of Thread in C!
Hello,World of Thread in C!
Hello,World of Thread in C!
Hello,World of Thread in C!
 * */

Test 2 簡單參數傳遞的線程併發編程實例

// 基於線程的併發編程,向線程傳遞參數1
// Test_2_createThread
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#define NUM_Threads 5

// 線程的運行函數
void *PrintHelloId(void *threadid)
{
    // 對傳入的參數進行強制類型轉換,由無類型指針變爲整形指針,而後再讀取
    int tid = *((int *)threadid);
    printf("Hello,World, Thread %d\n",tid);
    return 0;
}

int main()
{
    pthread_t pthreads[NUM_Threads];
    int i, rc;
    // 用數組存儲i的數值
    int indexes[NUM_Threads];

    for (i=0; i<NUM_Threads; i++)
    {
        printf("main() : 建立線程 %d \n",i);
        indexes[i] = i; // 保存i的數值
        // 在indexes傳入參數的時候必須轉換爲無類型指針
        rc = pthread_create(&pthreads[i], NULL, PrintHelloId, (void *)&indexes[i]);
        if (0 != rc)
        {
            printf("Error: 沒法建立線程!\n");
            exit(-1);
        }
    }

    pthread_exit(NULL);
    return 0;
}

/*
 * 在CLion(Ubuntu)中輸出結果是
main() : 建立線程 0
main() : 建立線程 1
Hello,World, Thread 0
main() : 建立線程 2
Hello,World, Thread 1
main() : 建立線程 3
Hello,World, Thread 2
main() : 建立線程 4
Hello,World, Thread 3
Hello,World, Thread 4
 * */

Test 3 結構體參數傳遞的線程併發編程實例

// 基於線程的併發編程,向線程傳遞參數2(傳遞結構體)
// Test_3_createThread
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#define NUM_Threads 5

typedef struct thread_data{
    int threadid;
    char message;
}THDATA,*PTHDATA;

void * PrintHello(void * pthreadid)
{
    PTHDATA tid = (PTHDATA)pthreadid;

    printf("This is Pthread : %d ;info : %c \n",tid->threadid, tid->message);

    return 0;
}

int main(void)
{
    pthread_t Pthread[NUM_Threads];
    THDATA index[NUM_Threads];
    int i, ret;

    for (i = 0; i < NUM_Threads; i++)
    {
        printf("main() : 建立線程 %d \n",i);
        index[i].threadid = i;
        index[i].message = 'A'+i%10;
        ret = pthread_create(&Pthread[i], NULL, PrintHello, (void *)&index[i]);
        if (0 != ret)
        {
            printf("Error: 建立線程失敗!\n");
            exit(-1);
        }
    }
    pthread_exit(NULL);
    return 0;
}

/*
 * 在CLion(Ubuntu)中輸出結果是
main() : 建立線程 0
main() : 建立線程 1
This is Pthread : 0 ;info : A
main() : 建立線程 2
main() : 建立線程 3
This is Pthread : 2 ;info : C
main() : 建立線程 4
This is Pthread : 3 ;info : D
This is Pthread : 4 ;info : E
This is Pthread : 1 ;info : B
 * */

Test 4 線程的鏈接編程實例

// 基於線程的併發編程,鏈接或分離線程
// Test_4_createThread
// 2019年10月27日14:45:11 還沒有徹底搞明白
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>

#define NUM_Pthread 5

void *PrintHello(void * pthreadid)
{
    int tid = *((int *)pthreadid);
    printf("Sleeping in thread %d ,...exiting \n",tid);
    return 0;
}

int main(void)
{
    int i, ret;
    pthread_t Pthread[NUM_Pthread];
    pthread_attr_t attr; // 定義線程屬性
    void * status;
    int index[NUM_Pthread];

    // 初始化並設置線程爲可鏈接
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

    for (i=0; i<NUM_Pthread; i++)
    {
        printf("main() : 建立線程 %d \n",i);
        index[i] = i;
        ret = pthread_create(&Pthread[i], NULL, PrintHello, (void *)&index[i]);
    }

    // 刪除屬性,並等待其餘線程
    pthread_attr_destroy(&attr);
    for (i=0; i<NUM_Pthread; i++)
    {
        ret = pthread_join(Pthread[i], status);
        if (0 != ret)
        {
            printf("Error: unable to join,%d\n",ret);
            exit(-1);
        }
        printf("main(): complete thread id : %d",i);
        printf(" exiting with status : %p\n",status);
    }

    printf("main() : program exiting.\n");
    pthread_exit(NULL);

    return 0;
}

信號量機制

Test 5 信號量同步進行寫入

// 用信號量進行同步
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <semaphore.h>

#define Len 100       // 設置輸入內容長度

sem_t bin_sem;
char work_area[Len]; // 存放輸入內容

void *Thread_func(void *arg)
{
    // 等待信號量有大於0的值而後退出
    sem_wait(&bin_sem);
    while (0 != strncmp("end", work_area, 3))
    {
        printf("Input %ld characters\n", strlen(work_area)-1);
    }
    return 0;
}

int main(void)
{
    int res;    // 存放命令的返回值
    pthread_t Pthread; // 建立線程
    void *thread_result; // 存放線程處理結果

    // 初始化信號量,並設置初始值爲0
    res = sem_init(&bin_sem, 0, 0);
    if (0 != res)
    {
        perror("Semaphore initialization failes");
        exit(EXIT_FAILURE);
    }
    // 建立新線程 0
    res = pthread_create(&Pthread, NULL, Thread_func, NULL);
    if (0 != res)
    {
        perror("Thread creation failed");
        exit(EXIT_FAILURE);
    }
    printf("Enter 'end' to finish\n");
    // 當工做區內不是以end開頭的字符串,則繼續輸入
    while (0 != strncmp("end", work_area, 3))
    {
        // 以標準輸入獲取輸入到工做區內
        fgets(work_area, Len, stdin);
        sem_post(&bin_sem);  // 信號量+1
    }
    printf("\n Waiting for thread to finish...\n");
    // 等待線程結束
    res = pthread_join(Pthread, &thread_result);
    if (0 != res)
    {
        perror("Thread join failed");
        exit(EXIT_FAILURE);
    }
    printf("Thread joined\n");
    sem_destroy(&bin_sem);  // 銷燬信號量
    exit(EXIT_SUCCESS);

    return 0;
}

Test 6 互斥信號量實現對臨界資源操做

// 用互斥信號量進行同步
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>

#define Len 3 // 自增計算次數
#define NUM_Pthread 5 // 設置線程的長度

int count = 1; // 在數據段共享資源,多個進程搶佔臨界資源
// 對於臨界資源,應該添加互斥鎖
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void *Thread_func(void *threadid)
{
    int tid = *((int *)threadid);
    int i, val;
    printf("Pthread ID : %d \n",tid);

    for (i=0; i<NUM_Pthread; i++)
    {
        pthread_mutex_lock(&mutex);
        val = count;
        printf("val = %d \n",val++);
        count = val;
        pthread_mutex_unlock(&mutex);
    }

    return 0;
}

int main(void)
{
    int res;    // 存放命令的返回值
    int i;
    pthread_t Pthread[NUM_Pthread]; // 建立線程
    int index[NUM_Pthread];

    for (i=0; i<NUM_Pthread; i++)
    {
        index[i] = i;
        // 建立線程
       res = pthread_create(&Pthread[i], NULL, Thread_func, (void *)&index[i]);
       if (0 != res)
       {
           printf("Error: 建立線程失敗!\n");
           exit(-1);
       }
    }

    for (i=0; i<NUM_Pthread; i++)
    {
        // 匯合線程
        pthread_join(Pthread[i], NULL);
    }
    printf("count = %d\n",count);
    pthread_exit(NULL);
    return 0;
}

// 在運行此程序無互斥鎖時,咱們不只獲得錯誤的答案,並且每次獲得的答案都不相同
// 分析
// 當多個對等線程在一個處理器上併發運行時,機器指令以某種順序完成,每一個併發執行定義了線程中指令的某種順序

參考博文:

相關文章
相關標籤/搜索