1、共用變數
1)線程儲存模型
線程由核心自動調度,每個線程都有它自己的線程上下文(thread context),包括一個惟一的整數線程ID(Thread ID,TID),棧,棧指標,程式計數器,通用目的寄存器和條件碼。每個線程和其他線程一起共用進程內容相關的剩餘部分,包括整個使用者的虛擬位址空間,它是由唯讀文本(代碼),讀/寫資料,堆以及所有的共用庫代碼和資料區域組成的,還有,線程也共用同樣的開啟檔案的集合。[1]
寄存器從不共用,而虛擬儲存空間總是共用。
The memory model for the separate thread stacks is not as clean(整齊清楚的). These stacks are contained in the stack area of the virtual address space, and are usually accessed independently by their respective threads. We say usually rather than always, because different thread stacks are not protected from other threads. So if a thread somehow manages to acquire a pointer to another thread’s stack, then it can read and write any part of that stack. 樣本29行中, where the peer threads reference the contents of the main thread’s stack indirectly through the global ptr variable.
2)將變數映射到儲存空間
和全域變數一樣,虛擬儲存空間的讀/寫地區只包含在程式中聲明的每個本地靜態變數的一個執行個體。每個線程的棧都包含它自己的所有本地自動變數的執行個體。
3)我們說變數v是共用的,若且唯若它的一個執行個體被一個以上的線程引用。
範例程式碼
/* $begin sharing */#include "csapp.h"#define N 2void *thread(void *vargp);char **ptr; /* global variable */int main() { int i; pthread_t tid; char *msgs[N] = {"Hello from foo", "Hello from bar" }; ptr = msgs; for (i = 0; i < N; i++) Pthread_create(&tid, NULL, thread, (void *)i); Pthread_exit(NULL); }void *thread(void *vargp) { int myid = (int)vargp; //cnt是共用的,而myid不是共用的 static int cnt = 0; printf("[%d]: %s (cnt=%d)\n", myid, ptr[myid], ++cnt);}/* $end sharing */
2、用訊號量同步
當對同一共用變數,有多個線程進行更新時,由於每一次更新,對該變數來說,都有“載入到寄存器,更新之,儲存寫回到儲存空間”這個過程,多個線程操作時,便會產生錯位,混亂的情況,有必要對共用變數作一保護,使這個更新操作具有原子性。
訊號量s是具有非頁整數值的全域變數,只能由兩種特殊的操作來處理,稱為P,V操作。
P(s):
while (s <= 0); s--;
V (s): s++;
The P operation waits for the semaphore s to become nonzero, and then decrements it.The V operation increments s.
1)基本思想是,將每個共用變數(或者相關共用變數集合)與一個訊號量s(初始值1)聯絡起來,然後用P(s),V(s)操作將相應的臨界區(一段代碼)包圍起來。以這種方法來保護共用變數的訊號量叫做二進位訊號量(binary semaphore),因為值總是1,0。
The definitions of P and V ensure that a running program can never enter a state where a properly initialized semaphore has a negative value.
11.4.4有posix訊號量的簡介。
2)二進位訊號量通常叫做互斥鎖,在互斥鎖上執行一個P操作叫做加鎖,V操作叫做解鎖;一個已經對一個互斥鎖加鎖而還沒有解鎖的線程被稱為佔用互斥鎖。
3、用訊號量來調度共用資源
這種情況下,一個線程用訊號量來通知另一個線程,程式狀態中的某個條件已經為真了。如生產者-消費者問題。
範例程式碼
#ifndef __SBUF_H__#define __SBUF_H__#include "csapp.h"/* $begin sbuft */typedef struct { int *buf; /* Buffer array */ int n; /* Maximum number of slots */ int front; /* buf[(front+1)%n] is first item */ int rear; /* buf[rear%n] is last item */ sem_t mutex; /* Protects accesses to buf */ sem_t slots; /* Counts available slots */ sem_t items; /* Counts available items */} sbuf_t;/* $end sbuft */void sbuf_init(sbuf_t *sp, int n);void sbuf_deinit(sbuf_t *sp);void sbuf_insert(sbuf_t *sp, int item);int sbuf_remove(sbuf_t *sp);#endif /* __SBUF_H__ *///source code/* $begin sbufc */#include "csapp.h"#include "sbuf.h"/* Create an empty, bounded, shared FIFO buffer with n slots *//* $begin sbuf_init */void sbuf_init(sbuf_t *sp, int n){ sp->buf = Calloc(n, sizeof(int)); sp->n = n; /* Buffer holds max of n items */ sp->front = sp->rear = 0; /* Empty buffer iff front == rear */ Sem_init(&sp->mutex, 0, 1); /* Binary semaphore for locking */ Sem_init(&sp->slots, 0, n); /* Initially, buf has n empty slots */ Sem_init(&sp->items, 0, 0); /* Initially, buf has zero data items */}/* $end sbuf_init *//* Clean up buffer sp *//* $begin sbuf_deinit */void sbuf_deinit(sbuf_t *sp){ Free(sp->buf);}/* $end sbuf_deinit *//* Insert item onto the rear of shared buffer sp *//* $begin sbuf_insert */void sbuf_insert(sbuf_t *sp, int item){ P(&sp->slots); /* Wait for available slot */ P(&sp->mutex); /* Lock the buffer */ sp->buf[(++sp->rear)%(sp->n)] = item; /* Insert the item */ V(&sp->mutex); /* Unlock the buffer */ V(&sp->items); /* Announce available item */}/* $end sbuf_insert *//* Remove and return the first item from buffer sp *//* $begin sbuf_remove */int sbuf_remove(sbuf_t *sp){ int item; P(&sp->items); /* Wait for available item */ P(&sp->mutex); /* Lock the buffer */ item = sp->buf[(++sp->front)%(sp->n)]; /* Remove the item */ V(&sp->mutex); /* Unlock the buffer */ V(&sp->slots); /* Announce available slot */ return item;}/* $end sbuf_remove *//* $end sbufc */
參考
[1] http://www.cnblogs.com/mydomain/archive/2011/07/10/2102147.html