1 什么是線程
線程是cpu調度的最小單位,在Linux 下 實現線程的方式為輕量級進程,復用進程的結構體,使用clone函數創建
2 線程安全
所謂線程安全,更確切的應該描述為內存安全
#include <stdio.h>
#include <pthread.h>
int a=1;
void* myfun(){for(int i=0;i<10000;i++){a++;}
}int main(){pthread_t t1, t2;
pthread_create(&t1, NULL,myfun, (void*)1);
pthread_create(&t2, NULL, myfun, (void*)2);
pthread_join(t1, NULL);
pthread_join(t2, NULL);}
上述代碼 按照預期應該是 a++20000次,而執行結果卻與預期不符,這就是典型的線程不安全
產生原因也很簡單,在現代操作系統下 cpu并不是直接訪問內存,每個cpu核心都有一個 獨立的L0 L1 緩存,以及共享的L2 緩存,在線程1 將 a 從主存寫入緩存時,進行++操作,此時a +1 并未回寫至主存, 此時線程2 去主存中讀取的值仍然為1, 并且 ++操作也非原子操作,多種可能都會導致結果不符合預期
如何實現線程安全
實現的方式有很多種
最簡單常見的為使用二進制信號量或者mutex,例如
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>//sem_t semaphore;//定義信號量
pthread_mutex_t mutex;
int a=1;
void* myfun(){for(int i=0;i<10000;i++){
pthread_mutex_lock(&mutex);
//sem_wait(&semaphore);a++;
pthread_mutex_unlock(&mutex);
//sem_post(&semaphore);
return NULL;}
}int main(){//sem_init(&semaphore, 0, 1);
pthread_t t1, t2;
pthread_create(&t1, NULL,myfun, (void*)1);
pthread_create(&t2, NULL, myfun, (void*)2);
pthread_join(t1, NULL);
pthread_join(t2, NULL);//sem_destroy(&semaphore);
pthread_mutex_destroy(&mutex)}
semaphore 與?mutex 在底層由硬件cpu的原子指令以及緩存一致性協議與內存屏障保證其原子性
二者簡單差異 為?semaphore 競爭失敗時線程處于自旋狀態,而mutex則讓出cpu,增加了更多的上線文切換
在java 中常見的?synchronized 以及LOCK 都是使用mutex實現,例如
public class createFun {public static final Integer integer =1;public static int a=0;public static void main(String[] args) throws Exception{Thread t1= new Thread(new Runnable() {@Overridepublic void run() {add();}});Thread t2= new Thread(new Runnable() {@Overridepublic void run() {add();}});t1.start();t2.start();t1.join();t2.join();System.out.println("a="+a);}public static void add(){for(int i=0;i<10000;i++) {synchronized (integer) {a++;}}}
}