A. 淺談linux 多線程編程和 windows 多線程編程的異同
很早以前就想寫寫linux下多線程編程和windows下的多線程編程了,但是每當寫時又不知道從哪個地方寫起,怎樣把自己知道的東西都寫出來,下面我就談談linux多線程及線程同步,並將它和windows的多線程進行比較,看看他們之間有什麼相同點和不同的地方。
其實最開始我是搞windows下編程的,包括windows編程,windows 驅動,包括usb驅動,ndis驅動,pci驅動,1394驅動等等,同時也一條龍服務,做windows下的應用程序開發,後面慢慢的我又對linux開發產生比較深的興趣和愛好,就轉到搞linux開發了。在接下來的我還會寫一些博客,主要是寫linux編程和windows編程的區別吧,現在想寫的是linux下usb驅動和windows下usb驅動開發的區別,這些都是後話,等我將linux多線程和windows多線程講解完後,我再寫一篇usb驅動,談談windows 和linux usb驅動的東東。好了,言歸正傳。開始將多線程了。
首先我們講講為什麼要採用多線程編程,其實並不是所有的程序都必須採用多線程,有些時候採用多線程,性能還沒有單線程好。所以我們要搞清楚,什麼時候採用多線程。採用多線程的好處如下:
(1)因為多線程彼此之間採用相同的地址空間,共享大部分的數據,這樣和多進程相比,代價比較節儉,因為多進程的話,啟動新的進程必須分配給它獨立的地址空間,這樣需要數據表來維護代碼段,數據段和堆棧段等等。
(2)多線程和多進程相比,一個明顯的優點就是線程之間的通信了,對不同進程來說,它們具有獨立的數據空間,要進行數據的傳遞只能通過通信的方式進行,這種方式不僅費時,而且很不方便。但是對於多線程就不一樣了。他們之間可以直接共享數據,比如最簡單的方式就是共享全局變數。但是共享全部變數也要注意哦,呵呵,必須注意同步,不然後果你知道的。呵呵。
(3)在多cpu的情況下,不同的線程可以運行不同的cpu下,這樣就完全並行了。
反正我覺得在這種情況下,採用多線程比較理想。比如說你要做一個任務分2個步驟,你為提高工作效率,你可以多線程技術,開辟2個線程,第一個線程就做第一步的工作,第2個線程就做第2步的工作。但是你這個時候要注意同步了。因為只有第一步做完才能做第2步的工作。這時,我們可以採用同步技術進行線程之間的通信。
針對這種情況,我們首先講講多線程之間的通信,在windows平台下,多線程之間通信採用的方法主要有:
(1)共享全局變數,這種方法是最容易想到的,呵呵,那就首先講講吧,比如說吧,上面的問題,第一步要向第2步傳遞收據,我們可以之間共享全局變數,讓兩個線程之間傳遞數據,這時主要考慮的就是同步了,因為你後面的線程在對數據進行操作的時候,你第一個線程又改變了數據的內容,你不同步保護,後果很嚴重的。你也知道,這種情況就是讀臟數據了。在這種情況下,我們最容易想到的同步方法就是設置一個bool flag了,比如說在第2個線程還沒有用完數據前,第一個線程不能寫入。有時在2個線程所需的時間不相同的時候,怎樣達到最大效率的同步,就比較麻煩了。咱們可以多開幾個緩沖區進行操作。就像生產者消費者一樣了。如果是2個線程一直在跑的,由於時間不一致,緩沖區遲早會溢出的。在這種情況下就要考慮了,是不讓數據寫入還是讓數據覆蓋掉老的數據,這時候就要具體問題具體分析了。就此打住,呵呵。就是用bool變數控制同步,linux 和windows是一樣的。
既然講道了這里,就再講講其它同步的方法。同樣 針對上面的這個問題,共享全局變數同步問題。除了採用bool變數外,最容易想到的方法就是互斥量了。呵呵,也就是傳說中的加鎖了。windows下加鎖和linux下加鎖是類似的。採用互斥量進行同步,要想進入那段代碼,就先必須獲得互斥量。
linux上互斥量的函數是:
windows下互斥量的函數有:createmutex 創建一個互斥量,然後就是獲得互斥量waitforsingleobject函數,用完了就釋放互斥量ReleaseMutex(hMutex),當減到0的時候 內核會才會釋放其對象。下面是windows下與互斥的幾個函數原型。
HANDLE WINAPI CreateMutex(
__in LPSECURITY_ATTRIBUTES lpMutexAttributes,
__in BOOL bInitialOwner,
__in LPCTSTR lpName
);
可以可用來創建一個有名或無名的互斥量對象
第一參數 可以指向一個結構體SECURITY_ATTRIBUTES一般可以設為null;
第二參數 指當時的函數是不是感應感應狀態 FALSE為當前擁有者不會創建互斥
第三參數 指明是否是有名的互斥對象 如果是無名 用null就好。
DWORD WINAPI WaitForSingleObject(
__in HANDLE hHandle,
__in DWORD dwMilliseconds
);
第一個是 創建的互斥對象的句柄。第二個是 表示將在多少時間之後返回 如果設為宏INFINITE 則不會返回 直到用戶自己定義返回。
對於linux操作系統,互斥也是類似的,只是函數不同罷了。在linux下,和互斥相關的幾個函數也要閃亮登場了。
pthread_mutex_init函數:初始化一個互斥鎖;
pthread_mutex_destroy函數:注銷一個互斥鎖;
pthread_mutex_lock函數:加鎖,如果不成功,阻塞等待;
pthread_mutex_unlock函數:解鎖;
pthread_mutex_trylock函數:測試加鎖,如果不成功就立即返回,錯誤碼為EBUSY;
至於這些函數的用法,google上一搜,就出來了,呵呵,在這里不多講了。windows下還有一個可以用來保護數據的方法,也是線程同步的方式
就是臨界區了。臨界區和互斥類似。它們之間的區別是,臨界區速度快,但是它只能用來同步同一個進程內的多個線程。臨界區的獲取和釋放函數如下:
EnterCriticalSection() 進入臨界區; LeaveCriticalSection()離開臨界區。 對於多線程共享內存的東東就講到這里了。
(2)採用消息機制進行多線程通信和同步,windows下面的的消息機制的函數用的多的就是postmessage了。Linux下的消息機制,我用的較少,就不在這里說了,如果誰熟悉的,也告訴我,呵呵。
(3)windows下的另外一種線程通信方法就是事件和信號量了。同樣針對我開始舉得例子,2個線程同步,他們之間傳遞信息,可以採用事件(Event)或信號量(Semaphore),比如第一個線程完成生產的數據後,就必須告訴第2個線程,他已經把數據准備好了,你可以來取走了。第2個線程就把數據取走。呵呵,這里可以採用消息機制,當第一個線程准備好數據後,就直接postmessage給第2個線程,按理說採用postmessage一個線程就可以搞定這個問題了。呵呵,不是重點,省略不講了。
對於linux,也有類似的方法,就是條件變數了,呵呵,這里windows和linux就有不同了。要特別講講才行。
對於windows,採用事件和信號量同步時候,都會使用waitforsingleobject進行等待的,這個函數的第一個參數是一個句柄,在這里可以是Event句柄,或Semaphore句柄,第2個參數就是等待的延遲,最終等多久,單位是ms,如果這個參數為INFINITE,那麼就是無限等待了。釋放信號量的函數為ReleaseSemaphore();釋放事件的函數為SetEvent。當然使用這些東西都要初始化的。這里就不講了。Msdn一搜,神馬都出來了,呵呵。神馬都是浮雲!
對於linux操作系統,是採用條件變數來實現類似的功能的。Linux的條件變數一般都是和互斥鎖一起使用的,主要的函數有:
pthread_mutex_lock ,
pthread_mutex_unlock,
pthread_cond_init
pthread_cond_signal
pthread_cond_wait
pthread_cond_timewait
為了和windows操作系統進行對比,我用以下表格進行比較:
對照以上表格,總結如下:
(1) Pthread_cleanup_push,Pthread_cleanup_pop:
這一對函數push和pop的作用是當出現異常退出時,做一些清除操作,即當在push和pop函數之間異常退出,包括調用pthread_exit退出,都會執行push裡面的清除函數,如果有多個push,注意是是棧,先執行後面的那個函數,在執行前面的函數,但是注意當在這2個函數之間通過return 退出的話,執不執行push後的函數就看pop函數中的參數是不是為0了。還有當沒有異常退出時,等同於在這裡面return退出的情況,即:當pop函數參數不為0時,執行清除操作,當pop函數參數為0時,不執行push函數中的清除函數。
(2)linux的pthread_cond_signal和SetEvent的不同點
Pthread_cond_singal釋放信號後,當沒有Pthread_cond_wait,信號馬上復位了,這點和SetEvent不同,SetEvent是不會復位的。詳解如下:
條件變數的置位和復位有2種常用模型:第一種模型是當條件變數置位時(signaled)以後,如果當前沒有線程在等待,其狀態會保持為置位(signaled),直到有等待的線程進入被觸發,其狀態才會變為unsignaled,這種模型以採用Windows平台上的Auto-set Event 為代表。
第2種模型則是Linux平台的pthread所採用的模型,當條件變數置位(signaled)以後,即使當前沒有任何線程在等待,其狀態也會恢復為復位(unsignaled)狀態。
條件變數在Linux平台上的這種模型很難說好壞,在實際應用中,我們可以對
代碼稍加改進就可以避免這種差異的發生。由於這種差異只會發生在觸發沒有被線程等待在條件變數的時刻,因此我們只需要掌握好觸發的時機即可。最簡單的做法是增加一個計數器記錄等待線程的個數,在決定觸發條件變數前檢查該變數即可。
示例 使用 pthread_cond_wait() 和 pthread_cond_signal()
pthread_mutex_t count_lock;
pthread_cond_t count_nonzero;
unsigned count;
decrement_count()
{
pthread_mutex_lock(&count_lock);
while (count == 0)
pthread_cond_wait(&count_nonzero, &count_lock);
count = count - 1;
pthread_mutex_unlock(&count_lock);
}
increment_count()
{
pthread_mutex_lock(&count_lock);
if (count == 0)
pthread_cond_signal(&count_nonzero);
count = count + 1;
pthread_mutex_unlock(&count_lock);
}
(3) 注意Pthread_cond_wait條件返回時互斥鎖的解鎖問題
extern int pthread_cond_wait __P ((pthread_cond_t *__cond,pthread_mutex_t *__mutex));
調用這個函數時,線程解開mutex指向的鎖並被條件變數cond阻塞。線程可以被函數pthread_cond_signal和函數 pthread_cond_broadcast喚醒線程被喚醒後,它將重新檢查判斷條件是否滿足,如果還不滿足,一般說來線程應該仍阻塞在這里,被等待被下一次喚醒。如果在多線程中採用pthread_cond_wait來等待時,會首先釋放互斥鎖,當等待的信號到來時,再次獲得互斥鎖,因此在之後要注意手動解鎖。舉例如下:
#include
#include
#include
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; /*初始化互斥鎖*/
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; //初始化條件變數
void *thread1(void *);
void *thread2(void *);
int i=1;
int main(void)
{
pthread_t t_a;
pthread_t t_b;
pthread_create(&t_a,NULL,thread1,(void *)NULL);/*創建進程t_a*/
pthread_create(&t_b,NULL,thread2,(void *)NULL); /*創建進程t_b*/
pthread_join(t_b, NULL);/*等待進程t_b結束*/
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
exit(0);
}
void *thread1(void *junk)
{
for(i=1;i<=9;i++)
{
printf("IN one\n");
pthread_mutex_lock(&mutex);//
if(i%3==0)
pthread_cond_signal(&cond);/*,發送信號,通知t_b進程*/
else
printf("thead1:%d\n",i);
pthread_mutex_unlock(&mutex);//*解鎖互斥量*/
printf("Up Mutex\n");
sleep(3);
}
}
void *thread2(void *junk)
{
while(i<9)
{
printf("IN two \n");
pthread_mutex_lock(&mutex);
if(i%3!=0)
pthread_cond_wait(&cond,&mutex);/*等待*/
printf("thread2:%d\n",i);
pthread_mutex_unlock(&mutex);
printf("Down Mutex\n");
sleep(3);
}
}
輸出如下:
IN one
thead1:1
Up Mutex
IN two
IN one
thead1:2
Up Mutex
IN one
thread2:3
Down Mutex
Up Mutex
IN one
thead1:4
Up Mutex
IN two
IN one
thead1:5
Up Mutex
IN one
Up Mutex
thread2:6
Down Mutex
IN two
thread2:6
Down Mutex
IN one
thead1:7
Up Mutex
IN one
thead1:8
Up Mutex
IN two
IN one
Up Mutex
thread2:9
Down Mutex
注意藍色的地方,有2個thread2:6,其實當這個程序多執行幾次,i=3和i=6時有可能多列印幾個,這里就是競爭鎖造成的了。
(4)另外要注意的Pthread_cond_timedwait等待的是絕對時間,這個和WaitForSingleObject是不同的,Pthread_cond_timedwait在網上也有討論。如下:這個問題比較經典,我把它搬過來。
thread_a :
pthread_mutex_lock(&mutex);
//do something
pthread_mutex_unlock(&mutex)
thread_b:
pthread_mutex_lock(&mutex);
//do something
pthread_cond_timedwait(&cond, &mutex, &tm);
pthread_mutex_unlock(&mutex)
有如上兩個線程thread_a, thread_b,現在如果a已經進入了臨界區,而b同時超時了,那麼b會從pthread_cond_timedwait返回嗎?如果能返回,那豈不是a,b都在臨界區?如果不能返回,那pthread_cond_timedwait的定時豈不是就不準了?
大家討論有價值的2點如下:
(1) pthread_cond_timedwait (pthread_cond_t *cv, pthread_mutex_t *external_mutex, const struct timespec *abstime) -- This function is a time-based variant of pthread_cond_wait. It waits up to abstime amount of time for cv to be notified. If abstime elapses before cv is notified, the function returns back to the caller with an ETIME result, signifying that a timeout has occurred. Even in the case of timeouts, the external_mutex will be locked when pthread_cond_timedwait returns.
(2) 2.1 pthread_cond_timedwait行為和pthread_cond_wait一樣,在返回的時候都要再次lock mutex.
2 .2pthread_cond_timedwait所謂的如果沒有等到條件變數,超時就返回,並不確切。
如果pthread_cond_timedwait超時到了,但是這個時候不能lock臨界區,pthread_cond_timedwait並不會立即返回,但是在pthread_cond_timedwait返回的時候,它仍在臨界區中,且此時返回值為ETIMEDOUT。
關於pthread_cond_timedwait超時返回的問題,我也認同觀點2。
附錄:
int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict_attr,void*(*start_rtn)(void*),void *restrict arg);
返回值:若成功則返回0,否則返回出錯編號
返回成功時,由tidp指向的內存單元被設置為新創建線程的線程ID。attr參數用於制定各種不同的線程屬性。新創建的線程從start_rtn函數的地址開始運行,該函數只有一個無指針參數arg,如果需要向start_rtn函數傳遞的參數不止一個,那麼需要把這些參數放到一個結構中,然後把這個結構的地址作為arg的參數傳入。
linux下用C開發多線程程序,Linux系統下的多線程遵循POSIX線程介面,稱為pthread。
由 restrict 修飾的指針是最初唯一對指針所指向的對象進行存取的方法,僅當第二個指針基於第一個時,才能對對象進行存取。對對象的存取都限定於基於由 restrict 修飾的指針表達式中。 由 restrict 修飾的指針主要用於函數形參,或指向由 malloc() 分配的內存空間。restrict 數據類型不改變程序的語義。 編譯器能通過作出 restrict 修飾的指針是存取對象的唯一方法的假設,更好地優化某些類型的常式。
第一個參數為指向線程標識符的指針。
第二個參數用來設置線程屬性。
第三個參數是線程運行函數的起始地址。
第四個參數是運行函數的參數。
因為pthread不是linux系統的庫,所以在編譯時注意加上-lpthread參數,以調用靜態鏈接庫。
終止線程:
如果在進程中任何一個線程中調用exit或_exit,那麼整個進行會終止,線程正常的退出方式有:
(1) 線程從啟動常式中返回(return)
(2) 線程可以被另一個進程終止(kill);
(3) 線程自己調用pthread_exit函數
#include
pthread_exit
線程等待:
int pthread_join(pthread_t tid,void **rval_ptr)
函數pthread_join用來等待一個線程的結束。函數原型為:
extern int pthread_join __P (pthread_t __th, void **__thread_return);
第一個參數為被等待的線程標識符,第二個參數為一個用戶定義的指針,它可以用來存儲被等待線程的返回值。這個函數是一個線程阻塞的函數,調用它的函數將一直等待到被等待的線程結束為止,當函數返回時,被等待線程的資源被收回。
對於windows線程的創建東西,就不列舉了,msdn上 一搜就出來了。呵呵。今天就講到這里吧,希望是拋磚引玉,大家一起探討,呵呵。部分內容我也是參考internet的,特此對原作者表示感謝!
B. Linux 多線程編程(二)2019-08-10
三種專門用於線程同步的機制:POSIX信號量,互斥量和條件變數.
在Linux上信號量API有兩組,一組是System V IPC信號量,即PV操作,另外就是POSIX信號量,POSIX信號量的名字都是以sem_開頭.
phshared參數指定信號量的類型,若其值為0,就表示這個信號量是當前進程的局部信號量,否則該信號量可以在多個進程之間共享.value值指定信號量的初始值,一般與下面的sem_wait函數相對應.
其中比較重要的函數sem_wait函數會以原子操作的方式將信號量的值減一,如果信號量的值為零,則sem_wait將會阻塞,信號量的值可以在sem_init函數中的value初始化;sem_trywait函數是sem_wait的非阻塞版本;sem_post函數將以原子的操作對信號量加一,當信號量的值大於0時,其他正在調用sem_wait等待信號量的線程將被喚醒.
這些函數成功時返回0,失敗則返回-1並設置errno.
生產者消費者模型:
生產者對應一個信號量:sem_t procer;
消費者對應一個信號量:sem_t customer;
sem_init(&procer,2)----生產者擁有資源,可以工作;
sem_init(&customer,0)----消費者沒有資源,阻塞;
在訪問公共資源前對互斥量設置(加鎖),確保同一時間只有一個線程訪問數據,在訪問完成後再釋放(解鎖)互斥量.
互斥鎖的運行方式:串列訪問共享資源;
信號量的運行方式:並行訪問共享資源;
互斥量用pthread_mutex_t數據類型表示,在使用互斥量之前,必須使用pthread_mutex_init函數對它進行初始化,注意,使用完畢後需調用pthread_mutex_destroy.
pthread_mutex_init用於初始化互斥鎖,mutexattr用於指定互斥鎖的屬性,若為NULL,則表示默認屬性。除了用這個函數初始化互斥所外,還可以用如下方式初始化:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER。
pthread_mutex_destroy用於銷毀互斥鎖,以釋放佔用的內核資源,銷毀一個已經加鎖的互斥鎖將導致不可預期的後果。
pthread_mutex_lock以原子操作給一個互斥鎖加鎖。如果目標互斥鎖已經被加鎖,則pthread_mutex_lock則被阻塞,直到該互斥鎖佔有者把它給解鎖.
pthread_mutex_trylock和pthread_mutex_lock類似,不過它始終立即返回,而不論被操作的互斥鎖是否加鎖,是pthread_mutex_lock的非阻塞版本.當目標互斥鎖未被加鎖時,pthread_mutex_trylock進行加鎖操作;否則將返回EBUSY錯誤碼。注意:這里討論的pthread_mutex_lock和pthread_mutex_trylock是針對普通鎖而言的,對於其他類型的鎖,這兩個加鎖函數會有不同的行為.
pthread_mutex_unlock以原子操作方式給一個互斥鎖進行解鎖操作。如果此時有其他線程正在等待這個互斥鎖,則這些線程中的一個將獲得它.
三個列印機輪流列印:
輸出結果:
如果說互斥鎖是用於同步線程對共享數據的訪問的話,那麼條件變數就是用於在線程之間同步共享數據的值.條件變數提供了一種線程之間通信的機制:當某個共享數據達到某個值時,喚醒等待這個共享數據的線程.
條件變數會在條件不滿足的情況下阻塞線程.且條件變數和互斥量一起使用,允許線程以無競爭的方式等待特定的條件發生.
其中pthread_cond_broadcast函數以廣播的形式喚醒所有等待目標條件變數的線程,pthread_cond_signal函數用於喚醒一個等待目標條件變數線程.但有時候我們可能需要喚醒一個固定的線程,可以通過間接的方法實現:定義一個能夠唯一標識目標線程的全局變數,在喚醒等待條件變數的線程前先設置該變數為目標線程,然後採用廣播的方式喚醒所有等待的線程,這些線程被喚醒之後都檢查該變數以判斷是否是自己.
採用條件變數+互斥鎖實現生產者消費者模型:
運行結果:
阻塞隊列+生產者消費者
運行結果:
C. 在linux下如何編程通過多線程方法在一個程序中實現伺服器與客戶端之間的tcp通信來傳輸文件
egegrergergerg
D. Linux下多線程和多進程程序的優缺點,各個適合什麼樣的業務場景
IBM有個傢伙做了個測試,發現切換線程context的時候,windows比linux快一倍多。進出最快的鎖(windows2k的 critical section和linux的pthread_mutex),windows比linux的要快五倍左右。當然這並不是說linux不好,而且在經過實際編程之後,綜合來看我覺得linux更適合做high performance server,不過在多線程這個具體的領域內,linux還是稍遜windows一點。這應該是情有可原的,畢竟unix家族都是從多進程過來的,而 windows從頭就是多線程的。
如果是UNIX/linux環境,採用多線程沒必要。
多線程比多進程性能高?誤導!
應該說,多線程比多進程成本低,但性能更低。
在UNIX環境,多進程調度開銷比多線程調度開銷,沒有顯著區別,就是說,UNIX進程調度效率是很高的。內存消耗方面,二者只差全局數據區,現在內存都很便宜,伺服器內存動輒若干G,根本不是問題。
多進程是立體交通系統,雖然造價高,上坡下坡多耗點油,但是不堵車。
多線程是平面交通系統,造價低,但紅綠燈太多,老堵車。
我們現在都開跑車,油(主頻)有的是,不怕上坡下坡,就怕堵車。
高性能交易伺服器中間件,如TUXEDO,都是主張多進程的。實際測試表明,TUXEDO性能和並發效率是非常高的。TUXEDO是貝爾實驗室的,與UNIX同宗,應該是對UNIX理解最為深刻的,他們的意見應該具有很大的參考意義。
多線程的優點:
無需跨進程邊界;
程序邏輯和控制方式簡單;
所有線程可以直接共享內存和變數等;
線程方式消耗的總資源比進程方式好;
多線程缺點:
每個線程與主程序共用地址空間,受限於2GB地址空間;
線程之間的同步和加鎖控制比較麻煩;
一個線程的崩潰可能影響到整個程序的穩定性;
到達一定的線程數程度後,即使再增加CPU也無法提高性能,例如Windows Server 2003,大約是1500個左右的線程數就快到極限了(線程堆棧設定為1M),如果設定線程堆棧為2M,還達不到1500個線程總數;
線程能夠提高的總性能有限,而且線程多了之後,線程本身的調度也是一個麻煩事兒,需要消耗較多的CPU
多進程優點:
每個進程互相獨立,不影響主程序的穩定性,子進程崩潰沒關系;
通過增加CPU,就可以容易擴充性能;
可以盡量減少線程加鎖/解鎖的影響,極大提高性能,就算是線程運行的模塊演算法效率低也沒關系;
每個子進程都有2GB地址空間和相關資源,總體能夠達到的性能上限非常大
多線程缺點:
邏輯控制復雜,需要和主程序交互;
需要跨進程邊界,如果有大數據量傳送,就不太好,適合小數據量傳送、密集運算
多進程調度開銷比較大;
最好是多進程和多線程結合,即根據實際的需要,每個CPU開啟一個子進程,這個子進程開啟多線程可以為若干同類型的數據進行處理。當然你也可以利用多線程+多CPU+輪詢方式來解決問題……
方法和手段是多樣的,關鍵是自己看起來實現方便有能夠滿足要求,代價也合適。
E. Linux下多線程和多進程程序的優缺點,各個適合什麼樣的業務場景
多進程比較安全,因為默認情況下不同進程之間的內存是獨立的(如果需要共享內存則需要進行進程間通信)。而多線程下,內存是共享的,這時就比較危險了,你要自己使用鎖、信號量等機制來解決內存塊的同時讀寫和同步等等。如果兩個功能沒有數據需要共享,或只有前後遞進關系,建議使用多進程。如果兩個功能需要同時對一塊數據進行處理(例如需要對資源進行創建和老化刪除),則需要使用多線程,這時可能需要使用鎖等機制來控制線程沖突。
F. 在linux書上看到,多線程編程需要防止同時訪問同一數據,保證數據讀寫安全。
1.運行一個進程中的多個線程,彼此之間使用相同的地址空間,共享大部分數據。
2.啟動一個線程所花費的空間遠遠小於啟動一個進程所話費的空間。
3.線程間切換所需要的時間遠遠小於進程間切換所需要的時間。
4.不同進程具有獨立的數據空間,數據的傳遞只能通過通信的方式。--費時、不方便
統一進程下的線程之間共享數據空間,一個線程數據可以直接為其他線程所用。--快捷、方便
5.編寫多線程需要注意的地方:
有的變數不能同時被兩個線程所修改
子程序中聲明為static的數據可能為多線程帶來災難性打擊
6.多線程優點:
1)提高應用程序相響應,將耗時長的操作置於一個新的線程,避免等待。
2)使CPU多核系統更加高效。
3)改善程序結構。將長而復雜的進程可以分為多個線程。
7. 進程是資源分配的基本單位,線程沒什麼資源。共享進程資源
8. volatile的作用是: 作為指令關鍵字,確保本條指令不會因編譯器的優化而省略,且要求每次直接讀值.
G. 如何看懂《Linux多線程服務端編程
一:進程和線程
每個進程有自己獨立的地址空間。「在同一個進程」還是「不在同一個進程」是系統功能劃分的重要決策點。《Erlang程序設計》[ERL]把進程比喻為人:
每個人有自己的記憶(內存),人與人通過談話(消息傳遞)來交流,談話既可以是面談(同一台伺服器),也可以在電話里談(不同的伺服器,有網路通信)。面談和電話談的區別在於,面談可以立即知道對方是否死了(crash,SIGCHLD),而電話談只能通過周期性的心跳來判斷對方是否還活著。
有了這些比喻,設計分布式系統時可以採取「角色扮演」,團隊里的幾個人各自扮演一個進程,人的角色由進程的代碼決定(管登錄的、管消息分發的、管買賣的等等)。每個人有自己的記憶,但不知道別人的記憶,要想知道別人的看法,只能通過交談(暫不考慮共享內存這種IPC)。然後就可以思考:
·容錯:萬一有人突然死了
·擴容:新人中途加進來
·負載均衡:把甲的活兒挪給乙做
·退休:甲要修復bug,先別派新任務,等他做完手上的事情就把他重啟
等等各種場景,十分便利。
線程的特點是共享地址空間,從而可以高效地共享數據。一台機器上的多個進程能高效地共享代碼段(操作系統可以映射為同樣的物理內存),但不能共享數據。如果多個進程大量共享內存,等於是把多進程程序當成多線程來寫,掩耳盜鈴。
「多線程」的價值,我認為是為了更好地發揮多核處理器(multi-cores)的效能。在單核時代,多線程沒有多大價值(個人想法:如果要完成的任務是CPU密集型的,那多線程沒有優勢,甚至因為線程切換的開銷,多線程反而更慢;如果要完成的任務既有CPU計算,又有磁碟或網路IO,則使用多線程的好處是,當某個線程因為IO而阻塞時,OS可以調度其他線程執行,雖然效率確實要比任務的順序執行效率要高,然而,這種類型的任務,可以通過單線程的」non-blocking IO+IO multiplexing」的模型(事件驅動)來提高效率,採用多線程的方式,帶來的可能僅僅是編程上的簡單而已)。Alan Cox說過:」A computer is a state machine.Threads are for people who can』t program state machines.」(計算機是一台狀態機。線程是給那些不能編寫狀態機程序的人准備的)如果只有一塊CPU、一個執行單元,那麼確實如Alan Cox所說,按狀態機的思路去寫程序是最高效的。
二:單線程伺服器的常用編程模型
據我了解,在高性能的網路程序中,使用得最為廣泛的恐怕要數」non-blocking IO + IO multiplexing」這種模型,即Reactor模式。
在」non-blocking IO + IO multiplexing」這種模型中,程序的基本結構是一個事件循環(event loop),以事件驅動(event-driven)和事件回調的方式實現業務邏輯:
[cpp] view plain
//代碼僅為示意,沒有完整考慮各種情況
while(!done)
{
int timeout_ms = max(1000, getNextTimedCallback());
int retval = poll(fds, nfds, timeout_ms);
if (retval<0){
處理錯誤,回調用戶的error handler
}else{
處理到期的timers,回調用戶的timer handler
if(retval>0){
處理IO事件,回調用戶的IO event handler
}
}
}
這里select(2)/poll(2)有伸縮性方面的不足(描述符過多時,效率較低),Linux下可替換為epoll(4),其他操作系統也有對應的高性能替代品。
Reactor模型的優點很明顯,編程不難,效率也不錯。不僅可以用於讀寫socket,連接的建立(connect(2)/accept(2)),甚至DNS解析都可以用非阻塞方式進行,以提高並發度和吞吐量(throughput),對於IO密集的應用是個不錯的選擇。lighttpd就是這樣,它內部的fdevent結構十分精妙,值得學習。
基於事件驅動的編程模型也有其本質的缺點,它要求事件回調函數必須是非阻塞的。對於涉及網路IO的請求響應式協議,它容易割裂業務邏輯,使其散布於多個回調函數之中,相對不容易理解和維護。
三:多線程伺服器的常用編程模型
大概有這么幾種:
a:每個請求創建一個線程,使用阻塞式IO操作。在java 1.4引人NIO之前,這是Java網路編程的推薦做法。可惜伸縮性不佳(請求太多時,操作系統創建不了這許多線程)。
b:使用線程池,同樣使用阻塞式IO操作。與第1種相比,這是提高性能的措施。
c:使用non-blocking IO + IO multiplexing。即Java NIO的方式。
d:Leader/Follower等高級模式。
在默認情況下,我會使用第3種,即non-blocking IO + one loop per thread模式來編寫多線程C++網路服務程序。
1:one loop per thread
此種模型下,程序里的每個IO線程有一個event loop,用於處理讀寫和定時事件(無論周期性的還是單次的)。代碼框架跟「單線程伺服器的常用編程模型」一節中的一樣。
libev的作者說:
One loop per thread is usually a good model. Doing this is almost never wrong, some times a better-performance model exists, but it is always a good start.
這種方式的好處是:
a:線程數目基本固定,可以在程序啟動的時候設置,不會頻繁創建與銷毀。
b:可以很方便地在線程間調配負載。
c:IO事件發生的線程是固定的,同一個TCP連接不必考慮事件並發。
Event loop代表了線程的主循環,需要讓哪個線程幹活,就把timer或IO channel(如TCP連接)注冊到哪個線程的loop里即可:對實時性有要求的connection可以單獨用一個線程;數據量大的connection可以獨佔一個線程,並把數據處理任務分攤到另幾個計算線程中(用線程池);其他次要的輔助性connections可以共享一個線程。
比如,在dbproxy中,一個線程用於專門處理客戶端發來的管理命令;一個線程用於處理客戶端發來的MySQL命令,而與後端資料庫通信執行該命令時,是將該任務分配給所有事件線程處理的。
對於non-trivial(有一定規模)的服務端程序,一般會採用non-blocking IO + IO multiplexing,每個connection/acceptor都會注冊到某個event loop上,程序里有多個event loop,每個線程至多有一個event loop。
多線程程序對event loop提出了更高的要求,那就是「線程安全」。要允許一個線程往別的線程的loop里塞東西,這個loop必須得是線程安全的。
在dbproxy中,線程向其他線程分發任務,是通過管道和隊列實現的。比如主線程accept到連接後,將表示該連接的結構放入隊列,並向管道中寫入一個位元組。計算線程在自己的event loop中注冊管道的讀事件,一旦有數據可讀,就嘗試從隊列中取任務。
2:線程池
不過,對於沒有IO而光有計算任務的線程,使用event loop有點浪費。可以使用一種補充方案,即用blocking queue實現的任務隊列:
[cpp] view plain
typedef boost::function<void()>Functor;
BlockingQueue<Functor> taskQueue; //線程安全的全局阻塞隊列
//計算線程
void workerThread()
{
while (running) //running變數是個全局標志
{
Functor task = taskQueue.take(); //this blocks
task(); //在產品代碼中需要考慮異常處理
}
}
// 創建容量(並發數)為N的線程池
int N = num_of_computing_threads;
for (int i = 0; i < N; ++i)
{
create_thread(&workerThread); //啟動線程
}
//向任務隊列中追加任務
Foo foo; //Foo有calc()成員函數
boost::function<void()> task = boost::bind(&Foo::calc,&foo);
taskQueue.post(task);
除了任務隊列,還可以用BlockingQueue<T>實現數據的生產者消費者隊列,即T是數據類型而非函數對象,queue的消費者從中拿到數據進行處理。其實本質上是一樣的。
3:總結
總結而言,我推薦的C++多線程服務端編程模式為:one (event) loop per thread + thread pool:
event loop用作IO multiplexing,配合non-blockingIO和定時器;
thread pool用來做計算,具體可以是任務隊列或生產者消費者隊列。
以這種方式寫伺服器程序,需要一個優質的基於Reactor模式的網路庫來支撐,muo正是這樣的網路庫。比如dbproxy使用的是libevent。
程序里具體用幾個loop、線程池的大小等參數需要根據應用來設定,基本的原則是「阻抗匹配」(解釋見下),使得CPU和IO都能高效地運作。所謂阻抗匹配原則:
如果池中線程在執行任務時,密集計算所佔的時間比重為 P (0 < P <= 1),而系統一共有 C 個 CPU,為了讓這 C 個 CPU 跑滿而又不過載,線程池大小的經驗公式 T = C/P。(T 是個 hint,考慮到 P 值的估計不是很准確,T 的最佳值可以上下浮動 50%)
以後我再講這個經驗公式是怎麼來的,先驗證邊界條件的正確性。
假設 C = 8,P = 1.0,線程池的任務完全是密集計算,那麼T = 8。只要 8 個活動線程就能讓 8 個 CPU 飽和,再多也沒用,因為 CPU 資源已經耗光了。
假設 C = 8,P = 0.5,線程池的任務有一半是計算,有一半等在 IO 上,那麼T = 16。考慮操作系統能靈活合理地調度 sleeping/writing/running 線程,那麼大概 16 個「50%繁忙的線程」能讓 8 個 CPU 忙個不停。啟動更多的線程並不能提高吞吐量,反而因為增加上下文切換的開銷而降低性能。
如果 P < 0.2,這個公式就不適用了,T 可以取一個固定值,比如 5*C。
另外,公式里的 C 不一定是 CPU 總數,可以是「分配給這項任務的 CPU 數目」,比如在 8 核機器上分出 4 個核來做一項任務,那麼 C=4。
四:進程間通信只用TCP
Linux下進程間通信的方式有:匿名管道(pipe)、具名管道(FIFO)、POSIX消息隊列、共享內存、信號(signals),以及Socket。同步原語有互斥器(mutex)、條件變數(condition variable)、讀寫鎖(reader-writer lock)、文件鎖(record locking)、信號量(semaphore)等等。
進程間通信我首選Sockets(主要指TCP,我沒有用過UDP,也不考慮Unix domain協議)。其好處在於:
可以跨主機,具有伸縮性。反正都是多進程了,如果一台機器的處理能力不夠,很自然地就能用多台機器來處理。把進程分散到同一區域網的多台機器上,程序改改host:port配置就能繼續用;
TCP sockets和pipe都是操作文件描述符,用來收發位元組流,都可以read/write/fcntl/select/poll等。不同的是,TCP是雙向的,Linux的pipe是單向的,進程間雙向通信還得開兩個文件描述符,不方便;而且進程要有父子關系才能用pipe,這些都限制了pipe的使用;
TCP port由一個進程獨占,且進程退出時操作系統會自動回收文件描述符。因此即使程序意外退出,也不會給系統留下垃圾,程序重啟之後能比較容易地恢復,而不需要重啟操作系統(用跨進程的mutex就有這個風險);而且,port是獨占的,可以防止程序重復啟動,後面那個進程搶不到port,自然就沒法初始化了,避免造成意料之外的結果;
與其他IPC相比,TCP協議的一個天生的好處是「可記錄、可重現」。tcpmp和Wireshark是解決兩個進程間協議和狀態爭端的好幫手,也是性能(吞吐量、延遲)分析的利器。我們可以藉此編寫分布式程序的自動化回歸測試。也可以用tcp之類的工具進行壓力測試。TCP還能跨語言,服務端和客戶端不必使用同一種語言。
分布式系統的軟體設計和功能劃分一般應該以「進程」為單位。從宏觀上看,一個分布式系統是由運行在多台機器上的多個進程組成的,進程之間採用TCP長連接通信。
使用TCP長連接的好處有兩點:一是容易定位分布式系統中的服務之間的依賴關系。只要在機器上運行netstat -tpna|grep <port>就能立刻列出用到某服務的客戶端地址(Foreign Address列),然後在客戶端的機器上用netstat或lsof命令找出是哪個進程發起的連接。TCP短連接和UDP則不具備這一特性。二是通過接收和發送隊列的長度也較容易定位網路或程序故障。在正常運行的時候,netstat列印的Recv-Q和Send-Q都應該接近0,或者在0附近擺動。如果Recv-Q保持不變或持續增加,則通常意味著服務進程的處理速度變慢,可能發生了死鎖或阻塞。如果Send-Q保持不變或持續增加,有可能是對方伺服器太忙、來不及處理,也有可能是網路中間某個路由器或交換機故障造成丟包,甚至對方伺服器掉線,這些因素都可能表現為數據發送不出去。通過持續監控Recv-Q和Send-Q就能及早預警性能或可用性故障。以下是服務端線程阻塞造成Recv-Q和客戶端Send-Q激增的例子:
[cpp] view plain
$netstat -tn
Proto Recv-Q Send-Q Local Address Foreign
tcp 78393 0 10.0.0.10:2000 10.0.0.10:39748 #服務端連接
tcp 0 132608 10.0.0.10:39748 10.0.0.10:2000 #客戶端連接
tcp 0 52 10.0.0.10:22 10.0.0.4:55572
五:多線程伺服器的適用場合
如果要在一台多核機器上提供一種服務或執行一個任務,可用的模式有:
a:運行一個單線程的進程;
b:運行一個多線程的進程;
c:運行多個單線程的進程;
d:運行多個多線程的進程;
考慮這樣的場景:如果使用速率為50MB/s的數據壓縮庫,進程創建銷毀的開銷是800微秒,線程創建銷毀的開銷是50微秒。如何執行壓縮任務?
如果要偶爾壓縮1GB的文本文件,預計運行時間是20s,那麼起一個進程去做是合理的,因為進程啟動和銷毀的開銷遠遠小於實際任務的耗時。
如果要經常壓縮500kB的文本數據,預計運行時間是10ms,那麼每次都起進程 似乎有點浪費了,可以每次單獨起一個線程去做。
如果要頻繁壓縮10kB的文本數據,預計運行時間是200微秒,那麼每次起線程似 乎也很浪費,不如直接在當前線程搞定。也可以用一個線程池,每次把壓縮任務交給線程池,避免阻塞當前線程(特別要避免阻塞IO線程)。
由此可見,多線程並不是萬靈丹(silver bullet)。
1:必須使用單線程的場合
據我所知,有兩種場合必須使用單線程:
a:程序可能會fork(2);
實際編程中,應該保證只有單線程程序能進行fork(2)。多線程程序不是不能調用fork(2),而是這么做會遇到很多麻煩:
fork一般不能在多線程程序中調用,因為Linux的fork只克隆當前線程的thread of control,不可隆其他線程。fork之後,除了當前線程之外,其他線程都消失了。
這就造成一種危險的局面。其他線程可能正好處於臨界區之內,持有了某個鎖,而它突然死亡,再也沒有機會去解鎖了。此時如果子進程試圖再對同一個mutex加鎖,就會立即死鎖。因此,fork之後,子進程就相當於處於signal handler之中(因為不知道調用fork時,父進程中的線程此時正在調用什麼函數,這和信號發生時的場景一樣),你不能調用線程安全的函數(除非它是可重入的),而只能調用非同步信號安全的函數。比如,fork之後,子進程不能調用:
malloc,因為malloc在訪問全局狀態時幾乎肯定會加鎖;
任何可能分配或釋放內存的函數,比如snprintf;
任何Pthreads函數;
printf系列函數,因為其他線程可能恰好持有stdout/stderr的鎖;
除了man 7 signal中明確列出的信號安全函數之外的任何函數。
因此,多線程中調用fork,唯一安全的做法是fork之後,立即調用exec執行另一個程序,徹底隔斷子進程與父進程的聯系。
在多線程環境中調用fork,產生子進程後。子進程內部只存在一個線程,也就是父進程中調用fork的線程的副本。
使用fork創建子進程時,子進程通過繼承整個地址空間的副本,也從父進程那裡繼承了所有互斥量、讀寫鎖和條件變數的狀態。如果父進程中的某個線程佔有鎖,則子進程同樣佔有這些鎖。問題是子進程並不包含佔有鎖的線程的副本,所以子進程沒有辦法知道它佔有了哪些鎖,並且需要釋放哪些鎖。
盡管Pthread提供了pthread_atfork函數試圖繞過這樣的問題,但是這回使得代碼變得混亂。因此《Programming With Posix Threads》一書的作者說:」Avoid using fork in threaded code except where the child process will immediately exec a new program.」。
b:限製程序的CPU佔用率;
這個很容易理解,比如在一個8核的伺服器上,一個單線程程序即便發生busy-wait,占滿1個core,其CPU使用率也只有12.5%,在這種最壞的情況下,系統還是有87.5%的計算資源可供其他服務進程使用。
因此對於一些輔助性的程序,如果它必須和主要服務進程運行在同一台機器的話,那麼做成單線程的能避免過分搶奪系統的計算資源。
H. 【求助】linux下的C語言多線程,怎樣監視鍵盤上的輸入
發所用語言為C..
一般的..要想學好嵌入式開發..就要兩個都會..
如果只學linux,這個只是為以後從事linux伺服器搭建,管理和維護等..差不多就是跟硬體打交道..
而嵌入式開發就相當於..在windows下用C,C++,C#,java等開發一樣..只不過他的開發平台換成了linux...
如果想自學建議按照以下步驟:
學習步驟如下:
1、Linux 基礎
安裝Linux操作系統
Linux文件系統
Linux常用命令
Linux啟動過程詳解
熟悉Linux服務能夠獨立安裝Linux操作系統
能夠熟練使用Linux系統的基本命令
認識Linux系統的常用服務安裝Linux操作系統
Linux基本命令實踐
設置Linux環境變數
定製Linux的服務 Shell 編程基礎使用vi編輯文件
使用Emacs編輯文件
使用其他編輯器
2、Shell 編程基礎
Shell簡介
認識後台程序
Bash編程熟悉Linux系統下的編輯環境
熟悉Linux下的各種Shell
熟練進行shell編程熟悉vi基本操作
熟悉Emacs的基本操作
比較不同shell的區別
編寫一個測試伺服器是否連通的shell腳本程序
編寫一個查看進程是否存在的shell腳本程序
編寫一個帶有循環語句的shell腳本程序
3、Linux 下的 C 編程基礎
linux C語言環境概述
Gcc使用方法
Gdb調試技術
Autoconf
Automake
Makefile
代碼優化 熟悉Linux系統下的開發環境
熟悉Gcc編譯器
熟悉Makefile規則編寫Hello,World程序
使用 make命令編譯程序
編寫帶有一個循環的程序
調試一個有問題的程序
4、嵌入式系統開發基礎
嵌入式系統概述
交叉編譯
配置TFTP服務
配置NFS服務
下載Bootloader和內核
嵌入式Linux應用軟體開發流程
熟悉嵌入式系統概念以及開發流程
建立嵌入式系統開發環境製作cross_gcc工具鏈
編譯並下載U-boot
編譯並下載Linux內核
編譯並下載Linux應用程序
嵌入式系統移植
Linux內核代碼
平台相關代碼分析
ARM平台介紹
平台移植的關鍵技術
移植Linux內核到 ARM平台 了解移植的概念
能夠移植Linux內核移植Linux2.6內核到 ARM9開發板
5、嵌入式 Linux 下串口通信
串列I/O的基本概念
嵌入式Linux應用軟體開發流程
Linux系統的文件和設備
與文件相關的系統調用
配置超級終端和MiniCOM 能夠熟悉進行串口通信
熟悉文件I/O 編寫串口通信程序
編寫多串口通信程序
6、嵌入式系統中多進程程序設計
Linux系統進程概述
嵌入式系統的進程特點
進程操作
守護進程
相關的系統調用了解Linux系統中進程的概念
能夠編寫多進程程序編寫多進程程序
編寫一個守護進程程序
sleep系統調用任務管理、同步與通信 Linux任務概述
任務調度
管道
信號
共享內存
任務管理 API 了解Linux系統任務管理機制
熟悉進程間通信的幾種方式
熟悉嵌入式Linux中的任務間同步與通信
編寫一個簡單的管道程序實現文件傳輸
編寫一個使用共享內存的程序
7、嵌入式系統中多線程程序設計
線程的基礎知識
多線程編程方法
線程應用中的同步問題了解線程的概念
能夠編寫簡單的多線程程序編寫一個多線程程序
8、嵌入式 Linux 網路編程
網路基礎知識
嵌入式Linux中TCP/IP網路結構
socket 編程
常用 API函數
分析Ping命令的實現
基本UDP套介面編程
許可證管理
PPP協議
GPRS 了解嵌入式Linux網路體系結構
能夠進行嵌入式Linux環境下的socket 編程
熟悉UDP協議、PPP協議
熟悉GPRS 使用socket 編寫代理伺服器
使用socket 編寫路由器
編寫許可證伺服器
指出TCP和UDP的優缺點
編寫一個web伺服器
編寫一個運行在 ARM平台的網路播放器
9、GUI 程序開發
GUI基礎
嵌入式系統GUI類型
編譯QT
進行QT開發熟悉嵌入式系統常用的GUI
能夠進行QT編程使用QT編寫「Hello,World」程序
調試一個加入信號/槽的實例
通過重載QWidget 類方法處理事件
10、Linux 字元設備驅動程序
設備驅動程序基礎知識
Linux系統的模塊
字元設備驅動分析
fs_operation結構
載入驅動程序了解設備驅動程序的概念
了解Linux字元設備驅動程序結構
能夠編寫字元設備驅動程序編寫Skull驅動
編寫鍵盤驅動
編寫I/O驅動
分析一個看門狗驅動程序
對比Linux2.6內核與2.4內核中字元設備驅動的不同
Linux 塊設備驅動程序塊設備驅動程序工作原理
典型的塊設備驅動程序分析
塊設備的讀寫請求隊列了解Linux塊設備驅動程序結構
能夠編寫簡單的塊設備驅動程序比較字元設備與塊設備的異同
編寫MMC卡驅動程序
分析一個文件系統
對比Linux2.6內核與2.4內核中塊設備驅動的不同
11、文件系統
虛擬文件系統
文件系統的建立
ramfs內存文件系統
proc文件系統
devfs 文件系統
MTD技術簡介
MTD塊設備初始化
MTD塊設備的讀寫操作了解Linux系統的文件系統
了解嵌入式Linux的文件系統
了解MTD技術
能夠編寫簡單的文件系統為 ARM9開發板添加 MTD支持
移植JFFS2文件系統
通過proc文件系統修改操作系統參數
分析romfs 文件系統源代碼
創建一個cramfs 文件系統
望採納:可是一個字一個字錢敲出來的..
另外,站長團上有產品團購,便宜有保證
I. Linux 的多線程編程中,如何給線程發信號
不管是在進程還是線程,很多時候我們都會使用一些定時器之類的功能,這里就定時器在多線程的使用說一下。首先在linux編程中定時器函數有alarm()和setitimer(),alarm()可以提供一個基於秒的定時功能,而setitimer可以提供一個基於微妙的定時功能。
alarm()原型:
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
這個函數在使用上很簡單,第一次調用這個函數的時候是設置定時器的初值,下一次調用是重新設置這個值,並會返回上一次定時的剩餘時間。
setitimer()原型:
#include <sys/time.h>
int setitimer(int which, const struct itimerval *value,struct itimerval *ovalue);
這個函數使用起來稍微有點說法,首先是第一個參數which的值,這個參數設置timer的計時策略,which有三種狀態分別是:
ITIMER_REAL:使用系統時間來計數,時間為0時發出SIGALRM信號,這種定時能夠得到一個精準的定時,當然這個定時是相對的,因為到了微秒級別我們的處理器本身就不夠精確。
ITIMER_VIRTUAL:使用進程時間也就是進程分配到的時間片的時間來計數,時間為0是發出SIGVTALRM信號,這種定時顯然不夠准確,因為系統給進程分配時間片不由我們控制。
ITIMER_PROF:上面兩種情況都能夠觸發
第二個參數參數value涉及到兩個結構體:
struct itimerval {
struct timeval it_interval; /* next value */
struct timeval it_value; /* current value */
};
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
在結構體itimerval中it_value是定時器當前的值,it_interval是當it_value的為0後重新填充的值。而timeval結構體中的兩個變數就簡單了一個是秒一個是微秒。
上面是這兩個定時函數的說明,這個函數使用本不是很難,可以說是很簡單,但是碰到具體的應用的時候可能就遇到問題了,在多進程編程中使用一般不會碰到什麼問題,這里說的這些問題主要體現在多線程編程中。比如下面這個程序:
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
void sig_handler(int signo)
{
alarm(2);
printf("alarm signal\n");
}
void *pthread_func()
{
alarm(2);
while(1)
{
pause();
}
}
int main(int argc, char **argv)
{
pthread_t tid;
int retval;
signal(SIGALRM, sig_handler);
if((retval = pthread_create(&tid, NULL, pthread_func, NULL)) < 0)
{
perror("pthread_create");
exit(-1);
}
while(1)
{
printf("main thread\n");
sleep(10);
}
return 0;
}
這個程序的理想結果是:
main thread
alarm signal
alarm signal
alarm signal
alarm signal
alarm signal
main thread
可事實上並不是這樣的,它的結果是:
main pthread
alarm signal
main pthread
alarm signal
main pthread
J. linux網路編程中如何實現伺服器端多個read()和客戶端write( )
TCP通信的模式如下圖,比較固定,對著圖編代碼就可以了:
因為客戶端沒有指定IP地址和埠,所以其IP和埠都是內核隨機分配的。