線程同步方式比較


用戶模式下的方法有:原子操作(例如一個單一的全局變量),臨界區。

內核模式下的方法有:事件,信號量,互斥量。

臨界區

保證在某一時刻只有一個線程能訪問數據的簡便辦法。在任意時刻只允許一個線程對共享資源進行訪問。如果有多個線程試圖同時訪問臨界區,那么 在有一個線程進入后其他所有試圖訪問此臨界區的線程將被掛起,並一直持續到進入臨界區的線程離開。臨界區在被釋放后,其他線程可以繼續搶占,並以此達到用原子方式操 作共享資源的目的。 僅能在同一進程內使用

互斥量 Mutex

互斥量跟臨界區很相似,只有擁有互斥對象的線程才具有訪問資源的權限,由於互斥對象只有一個,因此就決定了任何情況下此共享資源都不會同時被多個線程所訪問。當前占據資源的線程在任務處理完后應將擁有的互斥對象交出,以便其他線程在獲得后得以訪問資源。互斥量比臨界區復雜。因為使用互斥不僅僅能夠在同一應用程序不同線程中實現資源的安全共享,而且可以在不同應用程序的線程之間實現對資源的安全共享。 

信號量 

信號量對象對線程的同步方式與前面幾種方法不同,信號允許多個線程同時使用共享資源 ,這與操作系統中的PV操作相同。

事件(Event) 

事件機制,則允許一個線程在處理完一個任務后,主動喚醒另外一個線程執行任務。

 

以上幾種方法在win和linux下的API又有不同

Linux下實現同步的API:

互斥量 Mutex

  1. 初始化鎖。在Linux下,線程的互斥量數據類型是pthread_mutex_t。在使用前,要對它進行初始化。
    靜態分配:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    動態分配:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr_t *mutexattr);
  2. 加鎖。對共享資源的訪問,要對互斥量進行加鎖,如果互斥量已經上了鎖,調用線程會阻塞,直到互斥量被解鎖。
    int pthread_mutex_lock(pthread_mutex *mutex);
    int pthread_mutex_trylock(pthread_mutex_t *mutex);
  3. 解鎖。在完成了對共享資源的訪問后,要對互斥量進行解鎖。
    int pthread_mutex_unlock(pthread_mutex_t *mutex);
  4. 銷毀鎖。鎖在是使用完成后,需要進行銷毀以釋放資源。
    int pthread_mutex_destroy(pthread_mutex *mutex);

信號量(sem)

如同進程一樣,線程也可以通過信號量來實現通信,雖然是輕量級的。信號量函數的名字都以"sem_"打頭。線程使用的基本信號量函數有四個。

  1. 信號量初始化。
    int sem_init (sem_t *sem , int pshared, unsigned int value);
    這是對由sem指定的信號量進行初始化,設置好它的共享選項(linux 只支持為0,即表示它是當前進程的局部信號量),然后給它一個初始值VALUE。
  2. 等待信號量。給信號量減1,然后等待直到信號量的值大於0。
    int sem_wait(sem_t *sem);
  3. 釋放信號量。信號量值加1。並通知其他等待線程。
    int sem_post(sem_t *sem);
  4. 銷毀信號量。我們用完信號量后都它進行清理。歸還占有的一切資源。
    int sem_destroy(sem_t *sem);

條件變量(cond) 

互斥鎖不同,條件變量是用來等待而不是用來上鎖的。條件變量用來自動阻塞一個線程,直到某特殊情況發生為止。通常條件變量和互斥鎖同時使用。條件變量分為兩部分: 條件和變量。條件本身是由互斥量保護的。線程在改變條件狀態前先要鎖住互斥量。條件變量使我們可以睡眠等待某種條件出現。條件變量是利用線程間共享的全局變量進行同步的一種機制,主要包括兩個動作:一個線程等待"條件變量的條件成立"而掛起;另一個線程使"條件成立"(給出條件成立信號)。條件的檢測是在互斥鎖的保護下進行的。如果一個條件為假,一個線程自動阻塞,並釋放等待狀態改變的互斥鎖。如果另一個線程改變了條件,它發信號給關聯的條件變量,喚醒一個或多個等待它的線程,重新獲得互斥鎖,重新評價條件。如果兩進程共享可讀寫的內存,條件變量可以被用來實現這兩進程間的線程同步。

  1. 初始化條件變量。
    靜態態初始化,pthread_cond_t cond = PTHREAD_COND_INITIALIER;
    動態初始化,int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
  2. 等待條件成立。釋放鎖,同時阻塞等待條件變量為真才行。timewait()設置等待時間,仍未signal,返回ETIMEOUT(加鎖保證只有一個線程wait)
    int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
    int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const timespec *abstime);
  3. 激活條件變量。pthread_cond_signal,pthread_cond_broadcast(激活所有等待線程)
    int pthread_cond_signal(pthread_cond_t *cond);
    int pthread_cond_broadcast(pthread_cond_t *cond); //解除所有線程的阻塞
  4. 清除條件變量。無線程等待,否則返回EBUSY
    int pthread_cond_destroy(pthread_cond_t *cond);

參考  Linux 線程同步的三種方法

屏障

barrier(屏障)與互斥量,讀寫鎖,自旋鎖不同,它不是用來保護臨界區的。相反,它跟條件變量一樣,是用來協同多線程一起工作!!!

條件變量是多線程間傳遞狀態的改變來達到協同工作的效果。屏障是多線程各自做自己的工作,如果某一線程完成了工作,就等待在屏障那里,直到其他線程的工作都完成了,再一起做別的事。舉個通俗的例子:

1.對於條件變量。在接力賽跑里,1號隊員開始跑的時候,2,3,4號隊員都站着不動,直到1號隊員跑完一圈,把接力棒給2號隊員,2號隊員收到接力棒后就可以跑了,跑完再給3號隊員。這里這個接力棒就相當於條件變量,條件滿足后就可以由下一個隊員(線程)跑。

2.對於屏障。在百米賽跑里,比賽沒開始之前,每個運動員都在賽場上自由活動,有的熱身,有的喝水,有的跟教練談論。比賽快開始時,准備完畢的運動員就預備在起跑線上,如果有個運動員還沒准備完(除去特殊情況),他們就一直等,直到運動員都在起跑線上,裁判喊口號后再開始跑。這里的起跑線就是屏障,做完准備工作的運動員都等在起跑線,直到其他運動員也把准備工作做完!

1.創建屏障

1 #include <pthread.h>
2 
3 int pthread_barrier_init(pthread_barrier_t *restrict barrier, const pthread_barrierattr_t *restrict attr, unsigned count);

barrier:pthread_barrier_t結構體指針

attr:屏障屬性結構體指針

count:屏障等待的線程數目,即要count個線程都到達屏障時,屏障才解除,線程就可以繼續執行

2.等待

1 #include <pthread.h>
2 
3 int pthread_barrier_wait(pthread_barrier_t *barrier);

函數的成功返回值有2個,第一個成功返回的線程會返回PTHREAD_BARRIER_SERIAL_THREAD,其他線程都返回0。可以用第一個成功返回的線程來做一些善后處理工作。

3.銷毀屏障

1 #include <pthread.h>
2 
3 int pthread_barrier_destroy(pthread_barrier_t *barrier);

例子:

/**
 * @file pthread_barrier.c
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

/* 屏障總數 */
#define PTHREAD_BARRIER_SIZE 4

/* 定義屏障 */
pthread_barrier_t barrier;

void err_exit(const char *err_msg)
{
    printf("error:%s\n", err_msg);
    exit(1);
}

void *thread_fun(void *arg)
{
    int result;
    char *thr_name = (char *)arg;

    /* something work */

    printf("線程%s工作完成...\n", thr_name);

    /* 等待屏障 */
    result = pthread_barrier_wait(&barrier);
    if (result == PTHREAD_BARRIER_SERIAL_THREAD)
        printf("線程%s,wait后第一個返回\n", thr_name);
    else if (result == 0)
        printf("線程%s,wait后返回為0\n", thr_name);

    return NULL;
}

int main(void)
{
    pthread_t tid_1, tid_2, tid_3;

    /* 初始化屏障 */
    pthread_barrier_init(&barrier, NULL, PTHREAD_BARRIER_SIZE);

    if (pthread_create(&tid_1, NULL, thread_fun, "1") != 0)
        err_exit("create thread 1");

    if (pthread_create(&tid_2, NULL, thread_fun, "2") != 0)
        err_exit("create thread 2");

    if (pthread_create(&tid_3, NULL, thread_fun, "3") != 0)
        err_exit("create thread 3");

    /* 主線程等待工作完成 */
    pthread_barrier_wait(&barrier);
    printf("所有線程工作已完成...\n");

    sleep(1);
    return 0;
}
View Code

 

參考:http://www.cnblogs.com/yuuyuu/p/5152560.html

 

win下實現同步的API:

臨界區(Critical Section)

EnterCriticalSection() 進入臨界區

LeaveCriticalSection() 離開臨界區

互斥量(Mutex) 

CreateMutex() 創建一個互斥量 

OpenMutex() 打開一個互斥量 

ReleaseMutex() 釋放互斥量 

WaitForMultipleObjects() 等待互斥量對象 

信號量(Semaphores)

CreateSemaphore() 創建一個信號量

OpenSemaphore() 打開一個信號量

ReleaseSemaphore() 釋放信號量

WaitForSingleObject() 等待信號量

事件(Event)

CreateEvent() 創建一個事件 

OpenEvent() 打開一個事件

SetEvent() 回置事件

WaitForSingleObject() 等待一個事件

WaitForMultipleObjects()   等待多個事件

win下互斥量和臨界區區別 

 

 

Mutex

Critical Section

性能和速度

慢。

Mutex 是內核對象,相關函數的執行 (WaitForSingleObject,

ReleaseMutex)需要用戶模式(User Mode)到內核模式

(Kernel Mode)的轉換,在x86處理器上這種轉化一般要

發費600個左右的 CPU指令周期。

快。

Critical Section本身不是內核對象,相關函數

(EnterCriticalSection,LeaveCriticalSection)

的調用一般都在用戶模式內執行,在x86處理器上

一般只需要發費9個左右的 CPU指令周期。只有

當想要獲得的鎖正好被別的線程擁有時才會退化

成和Mutex一樣,即轉換到內核模式,發費600個

左右的 CPU指令周期。

能否跨越進程(Process)邊界

可以

不可

定義寫法

HANDLE hmtx;

CRITICAL_SECTION cs;

初始化寫法

hmtx= CreateMutex (NULL, FALSE, NULL);

InitializeCriticalSection(&cs);

結束清除寫法

CloseHandle(hmtx);

DeleteCriticalSection(&cs);

無限期等待的寫法

WaitForSingleObject (hmtx, INFINITE);

EnterCriticalSection(&cs);

0等待(狀態檢測)的寫法

WaitForSingleObject (hmtx, 0);

TryEnterCriticalSection(&cs);

任意時間等待的寫法

WaitForSingleObject (hmtx, dwMilliseconds);

不支持

鎖釋放的寫法

ReleaseMutex(hmtx);

LeaveCriticalSection(&cs);

能否被一道用於等待其他內核對象

可以(使用WaitForMultipleObjects,

WaitForMultipleObjectsEx,

MsgWaitForMultipleObjects,

MsgWaitForMultipleObjectsEx等等)

不可

當擁有鎖的線程死亡時

Mutex變成abandoned狀態,其他的等待線程可以獲得鎖。

Critical Section的狀態不可知(undefined),

以后的動作就不能保證了。

自己會不會鎖住自己

不會(對已獲得的Mutex,重復調用WaitForSingleObject不會

鎖住自己。但最后你別忘了要調用同樣次數的

ReleaseMutex)

不會(對已獲得的Critical Section,重復調用

EnterCriticalSection不會鎖住自己。但最后

你別忘了要調用同樣次數的

LeaveCriticalSection)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM