日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

Linux下多线程编程中信号量介绍及简单使用

發布時間:2023/11/27 生活经验 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux下多线程编程中信号量介绍及简单使用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在Linux中有兩種方法用于處理線程同步:信號量和互斥量。

線程的信號量是一種特殊的變量,它可以被增加或減少,但對其的關鍵訪問被保證是原子操作。如果一個程序中有多個線程試圖改變一個信號量的值,系統將保證所有的操作都將依次進行。信號量一般常用于保護一段代碼,使其每次只被一個執行線程運行。信號量是用來調協線程對共享資源的訪問的。

通過使用信號量可以很好的完成線程同步。兩個線程同時監視同一個信號量。A線程增加信號量的值,B線程減少信號量的值。當A線程增加信號量大于0時,B線程的等待信號量就會觸發,每觸發一次將信號量減1,直到將信號量減為0,B線程繼續等待A線程增加信號量。

信號量和互斥鎖(mutex)的區別:互斥鎖只允許一個線程進入臨界區,而信號量允許多個線程同時進入臨界區。

信號量用在多線程多任務同步的,一個線程完成了某一個動作就通過信號量告訴別的線程,別的線程再進行某些動作。而互斥鎖是用在多線程多任務互斥的,一個線程占用了某一個資源,那么別的線程就無法訪問,直到這個線程unlock,其他的線程才開始可以利用這個資源。比如對全局變量的訪問,有時要加鎖,操作完了,在解鎖。有的時候鎖和信號量會同時使用的。

信號量:只要信號量的value大于0,其他線程就可以sem_wait成功,成功后信號量的value減1。若value值不大于0,則sem_wait使得線程阻塞,直到sem_post釋放后value值加1,但是sem_wait返回之前還是會將此value值減1.

如果信號量的值大于0表示可用的資源數,小于0表示阻塞的線程數。

互斥鎖: 只要被鎖住,其他任何線程都不可以訪問被保護的資源,也就是說,信號量不一定是鎖定某一個資源,而是流程上的概念,比如:有A,B兩個線程,B線程要等A線程完成某一任務以后再進行自己下面的步驟,這個任務并不一定是鎖定某一資源,還可以是進行一些計算或者數據處理之類。而線程互斥量則是“鎖住某一資源”的概念,在鎖定期間內,其他線程無法對被保護的數據進行操作。在有些情況下兩者可以互換。

信號量是一個特殊類型的變量,它可以被增加或減少,但對它的訪問都會被保證是原子操作,即使在一個多線程程序中也是如此。也就是說,如果一個程序中有兩個或多個線程試圖改變一個信號量的值,系統將保證所有的操作都將依次進行。如果換成普通的變量,來自同一個程序中的不同線程的沖突操作將會導致不確定的操作。

兩種信號量:二進制信號量和計數信號量。二進制信號量只有0和1兩種取值,而計數信號量則有更大的取值范圍。如果某個共享資源只能被一個線程訪問,那么二進制信號量則是最好的打算;如果有多個線程需要訪問共享資源呢,使用計數信號量則是個好的主意。

互斥鎖只有0,1兩中狀態,適合于線程對共享資源的獨占訪問,很多時候每個資源可以同時被有限的線程訪問,此時互斥鎖將無法滿足;條件變量同步也同樣存在這種問題。信號量實際是一種非負整型計數器,可以很好的控制線程之間資源訪問,互斥鎖能實現的功能,信號量同樣可以。

信號量控制資源共享主要是PV原語操作, PV原語是對整數計數器信號量sem的操作。一次P操作使sem減一,而一次V操作使sem加一。進程(或線程)根據信號量的值來判斷是否對公共資源具有訪問權限。當信號量sem的值大于等于零時,該進程(或線程)具有公共資源的訪問權限;相反,當信號量sem的值小于零時,該進程(或線程)就將阻塞直到信號量sem的值大于等于0為止。

信號量的函數都以sem_開頭,線程中使用的基本信號量函數有4個,它們都聲明在頭文件semaphore.h中。

sem_init(sem_t*sem, int pshared, unsigned int value):該函數用于創建信號量。初始化一個定位在sem的匿名信號量。value參數指定信號量的初始值。pshared參數指明信號量是由進程內線程共享,還是由進程之間共享。如果pshared的值為0,那么信號量將被進程內的線程共享,并且應該放置在所有線程都可見的地址上(如全局變量,或者堆上動態分配的變量)。

sem_wait(sem_t*sem):該函數用于以原子操作的方式將信號量的值減1。(原子操作就是,如果兩個線程企圖同時給一個信號量加1或減1,它們之間不會互相干擾。)但它永遠會先等待該信號量為一個非零值才開始做減法。也就是說,如果你對一個值為2的信號量調用sem_wait(),線程將會繼續執行,這信號量的值將減到1。如果對一個值為0的信號量調用sem_wait(),這個函數就會等待直到有其它線程增加了這個值使它不再是0為止。如果有兩個線程都在sem_wait()中等待同一個信號量變成非零值,那么當它被第三個線程增加一個“1”時,等待線程中只有一個能夠對信號量做減法并繼續執行,另一個還將處于等待狀態。被用來阻塞當前線程直到信號量sem的值大于0,解除阻塞后將sem的值減一,表明公共資源經使用后減少。

sem_post(sem_t*sem):該函數用于以原子操作的方式將信號量的值加1。用來增加信號量的值當有線程阻塞在這個信號量上時,調用這個函數會使其中的一個線程不再阻塞,選擇機制同樣是由線程的調度策略決定的。它信號量的值加1同時發出信號來喚醒等待的線程。

sem_destroy:該函數用于對用完的信號量的清理。用來釋放信號量sem。

下面是從其他文章中copy的測試代碼,詳細內容介紹可以參考對應的reference:

test_thread_sem.cpp

// reference: https://software.intel.com/zh-cn/blogs/2011/12/02/linux-3
#include <iostream>
#include <pthread.h>
#include <semaphore.h>
#include <string.h>namespace {int g_Flag = 0;
sem_t sem_mutex; // 用于互斥
sem_t sem_syn; // 用于同步void *thread1(void *arg)
{fprintf(stdout, "Enter thread1\n");fprintf(stdout, "thread1 id: %u, g_Flag: %d\n", (unsigned int)pthread_self(), g_Flag);if (sem_wait(&sem_mutex) != 0) {fprintf(stderr, "pthread1 sem_mutex fail\n");return nullptr;}if (g_Flag == 2)sem_post(&sem_syn);g_Flag = 1;if (sem_post(&sem_mutex) != 0) {fprintf(stderr, "pthread1 sem_post fail\n");return nullptr;}fprintf(stdout, "thread1 id: %u, g_Flag: %d\n", (unsigned int)pthread_self(), g_Flag);fprintf(stdout, "Leave thread1\n");pthread_t tid = pthread_self();fprintf(stdout, "thread1 tid = %u\n", tid);pthread_join(tid, nullptr);fprintf(stdout, "\n");return nullptr;
}void *thread2(void *arg)
{fprintf(stdout, "Enter thread2\n");fprintf(stdout, "thread2 id: %u , g_Flag: %d\n", (unsigned int)pthread_self(), g_Flag);if (sem_wait(&sem_mutex) != 0) {fprintf(stderr, "thread2 sem_wait fail\n");return nullptr;}if (g_Flag == 1)sem_post(&sem_syn);g_Flag = 2;if (sem_post(&sem_mutex) != 0) {fprintf(stderr, "thread2 sem_post fail\n");return nullptr;}fprintf(stdout, "thread2 id: %u , g_Flag: %d\n", (unsigned int)pthread_self(), g_Flag);fprintf(stdout, "Leave thread2\n");pthread_t tid = pthread_self();fprintf(stdout, "thread2 tid = %u\n", tid);pthread_join(tid, nullptr);fprintf(stdout, "\n");return nullptr;
}} // namespaceint main()
{pthread_t tid1, tid2;sem_init(&sem_mutex, 0, 1);sem_init(&sem_syn, 0, 0);fprintf(stdout, "Inter main!\n");int ret = pthread_create(&tid2, nullptr, thread2, nullptr);if (ret != 0) {fprintf(stderr, "%s, %d\n", __func__, strerror(ret));return -1;}ret = pthread_create(&tid1, nullptr, thread1, nullptr);if (ret != 0) {fprintf(stderr, "%s, %d\n", __func__, strerror(ret));return -1;}fprintf(stdout, "Leave main!\n\n");sem_wait(&sem_syn); // 同步等待,阻塞return 0;
}

test_thread_sem1.cpp

// reference: http://man7.org/linux/man-pages/man3/sem_wait.3.html#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <time.h>
#include <assert.h>
#include <errno.h>
#include <signal.h>namespace {sem_t sem;#define handle_error(msg) \do { perror(msg); exit(EXIT_FAILURE); } while (0)void handler(int sig)
{write(STDOUT_FILENO, "sem_post() from handler\n", 24);if (sem_post(&sem) == -1) {write(STDERR_FILENO, "sem_post() failed\n", 18);_exit(EXIT_FAILURE);}
}} // namespaceint main(int argc, char *argv[])
{struct sigaction sa;struct timespec ts;int s;if (argc != 3) {fprintf(stderr, "Usage: %s <alarm-secs> <wait-secs>\n", argv[0]);exit(EXIT_FAILURE);}if (sem_init(&sem, 0, 0) == -1)handle_error("sem_init");/* Establish SIGALRM handler; set alarm timer using argv[1] */sa.sa_handler = handler;sigemptyset(&sa.sa_mask);sa.sa_flags = 0;if (sigaction(SIGALRM, &sa, nullptr) == -1)handle_error("sigaction");alarm(atoi(argv[1]));/* Calculate relative interval as current time plusnumber of seconds given argv[2] */if (clock_gettime(CLOCK_REALTIME, &ts) == -1)handle_error("clock_gettime");ts.tv_sec += atoi(argv[2]);printf("main() about to call sem_timedwait()\n");while ((s = sem_timedwait(&sem, &ts)) == -1 && errno == EINTR)continue;       /* Restart if interrupted by handler *//* Check what happened */if (s == -1) {if (errno == ETIMEDOUT)printf("sem_timedwait() timed out\n");elseperror("sem_timedwait");} elseprintf("sem_timedwait() succeeded\n");exit((s == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
}// ./test_thread_sem1 2 3
// ./test_thread_sem1 2 1

test_thread_sem2.cpp

// reference: https://mahaveerdarade.wordpress.com/2013/09/16/semaphores-in-linux-sem_wait-sem_post-code-examples-in-c/
#include <stdlib.h>
#include <pthread.h> 
#include <stdio.h>
#include <semaphore.h>
#include <unistd.h>namespace {int cnt = 0;
int a[] = {1,2,3,4,5,6,7,8,9};
char arr[] = {'a','b','c','d','e','f','g','h','j'};
sem_t s1;void* pc(void* arg)
{int i = 0;while (i < 9) {fprintf(stdout, "---- Line: %d\n", __LINE__);sem_wait(&s1);while (cnt == 0) {//fprintf(stdout, "---- Line: %d\n", __LINE__);	// 注意:打開此條語句對結果的影響sem_post(&s1);}fprintf(stdout, "%c\n", arr[i++]);sleep(1);cnt=0;sem_post(&s1);}return nullptr;
}void* pi(void* arg)
{int i = 0;while (i < 9) {sleep(2);		fprintf(stdout, "++++ Line: %d\n", __LINE__);		sem_wait(&s1);while (cnt == 1) {sem_post(&s1);}fprintf(stdout, "%d\n", a[i++]);sleep(1);cnt = 1;sem_post(&s1);}return nullptr;
}} // namespaceint main()
{pthread_t t1,t2;sem_init(&s1, 0, 1);pthread_create(&t1, nullptr, pc, nullptr);pthread_create(&t2, nullptr, pi, nullptr);pthread_join(t1, nullptr);pthread_join(t2, nullptr);sem_destroy(&s1);return 0;
}

test_thread_sem3.cpp

// reference: http://www.amparo.net/ce155/sem-ex.html
/* Includes */
#include <unistd.h>     /* Symbolic Constants */
#include <sys/types.h>  /* Primitive System Data Types */ 
#include <errno.h>      /* Errors */
#include <stdio.h>      /* Input/Output */
#include <stdlib.h>     /* General Utilities */
#include <pthread.h>    /* POSIX Threads */
#include <string.h>     /* String handling */
#include <semaphore.h>  /* Semaphore */namespace {/* global vars */
/* semaphores are declared global so they can be accessed in main() and in thread routine,here, the semaphore is used as a mutex */
sem_t mutex;
int counter = 0; /* shared variable *//* prototype for thread routine */
void* handler(void* ptr)
{int x; x = *((int *)ptr);printf("Thread %d: Waiting to enter critical region...\n", x);sem_wait(&mutex);       /* down semaphore *//* START CRITICAL REGION */printf("Thread %d: Now in critical region...\n", x);printf("Thread %d: Counter Value: %d\n", x, counter);printf("Thread %d: Incrementing Counter...\n", x);counter++;printf("Thread %d: New Counter Value: %d\n", x, counter);printf("Thread %d: Exiting critical region...\n", x);/* END CRITICAL REGION */    sem_post(&mutex);       /* up semaphore */pthread_exit(0); /* exit thread */
}} // namespaceint main()
{int i[2];pthread_t thread_a;pthread_t thread_b;i[0] = 0; /* argument to threads */i[1] = 1;sem_init(&mutex, 0, 1);      /* initialize mutex to 1 - binary semaphore *//* second param = 0 - semaphore is local *//* Note: you can check if thread has been successfully created by checking return value ofpthread_create */                                 pthread_create(&thread_a, NULL, handler, (void*)&i[0]);pthread_create(&thread_b, NULL, handler, (void*)&i[1]);pthread_join(thread_a, NULL);pthread_join(thread_b, NULL);sem_destroy(&mutex); /* destroy semaphore *//* exit */  exit(0);
} /* main() */


GitHub: https://github.com/fengbingchun/Linux_Code_Test

總結

以上是生活随笔為你收集整理的Linux下多线程编程中信号量介绍及简单使用的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。