亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

ios多線程和進程的區別(轉載)

2019-11-14 20:17:05
字體:
來源:轉載
供稿:網友

很想寫點關于多進程和多線程的東西,我確實很愛他們。但是每每想動手寫點關于他們的東西,卻總是求全心理作祟,始終動不了手。

今天終于下了決心,寫點東西,以后可以再修修補補也無妨。

 

一.為何需要多進程(或者多線程),為何需要并發?

這個問題或許本身都不是個問題。但是對于沒有接觸過多進程編程的朋友來說,他們確實無法感受到并發的魅力以及必要性。

我想,只要你不是整天都寫那種int main()到底的代碼的人,那么或多或少你會遇到代碼響應不夠用的情況,也應該有嘗過并發編程的甜頭。就像一個快餐點的服務員,既要在前臺接待客戶點餐,又要接電話送外賣,沒有分身術肯定會忙得你焦頭爛額的。幸運的是確實有這么一種技術,讓你可以像孫悟空一樣分身,靈魂出竅,樂哉樂哉地輕松應付一切狀況,這就是多進程/線程技術。

并發技術,就是可以讓你在同一時間同時執行多條任務的技術。你的代碼將不僅僅是從上到下,從左到右這樣規規矩矩的一條線執行。你可以一條線在main函數里跟你的客戶交流,另一條線,你早就把你外賣送到了其他客戶的手里。

 

所以,為何需要并發?因為我們需要更強大的功能,提供更多的服務,所以并發,必不可少。

 

二.多進程

什么是進程。最直觀的就是一個個pid,官方的說法就:進程是程序在計算機上的一次執行活動。

說得簡單點,下面這段代碼執行的時候

  1. int main()
  2. {
  3. PRintf(”pid is %d/n”,getpid() );
  4. return 0;
  5. }
  1. int main()
  2. {
  3. printf(”pid is %d/n”,getpid() );
  4. return 0;
  5. }

 

進入main函數,這就是一個進程,進程pid會打印出來,然后運行到return,該函數就退出,然后由于該函數是該進程的唯一的一次執行,所以return后,該進程也會退出。

 

看看多進程。linux下創建子進程的調用是fork();

 

  1. #include <unistd.h>
  2. #include <sys/types.h>
  3. #include <stdio.h>
  4. void print_exit()
  5. {
  6. printf("the exit pid:%d/n",getpid() );
  7. }
  8. main ()
  9. {
  10. pid_t pid;
  11. atexit( print_exit ); //注冊該進程退出時的回調函數
  12. pid=fork();
  13. if (pid < 0)
  14. printf("error in fork!");
  15. else if (pid == 0)
  16. printf("i am the child process, my process id is %d/n",getpid());
  17. else
  18. {
  19. printf("i am the parent process, my process id is %d/n",getpid());
  20. sleep(2);
  21. wait();
  22. }
  23. }
  1. #include <unistd.h>
  2. #include <sys/types.h>
  3. #include <stdio.h>
  4. void print_exit()
  5. {
  6. printf("the exit pid:%d/n",getpid() );
  7. }
  8. main ()
  9. {
  10. pid_t pid;
  11. atexit( print_exit ); //注冊該進程退出時的回調函數
  12. pid=fork();
  13. if (pid < 0)
  14. printf("error in fork!");
  15. else if (pid == 0)
  16. printf("i am the child process, my process id is %d/n",getpid());
  17. else
  18. {
  19. printf("i am the parent process, my process id is %d/n",getpid());
  20. sleep(2);
  21. wait();
  22. }
  23. }

 

i am the child process, my process id is 15806 the exit pid:15806 i am the parent process, my process id is 15805 the exit pid:15805

這是gcc測試下的運行結果。

 

關于fork函數,功能就是產生子進程,由于前面說過,進程就是執行的流程活動。

那么fork產生子進程的表現就是它會返回2次,一次返回0,順序執行下面的代碼。這是子進程。

一次返回子進程的pid,也順序執行下面的代碼,這是父進程。

(為何父進程需要獲取子進程的pid呢?這個有很多原因,其中一個原因:看最后的wait,就知道父進程等待子進程的終結后,處理其task_struct結構,否則會產生僵尸進程,扯遠了,有興趣可以自己google)。

如果fork失敗,會返回-1.

額外說下atexit( print_exit ); 需要的參數肯定是函數的調用地址。

這里的print_exit 是函數名還是函數指針呢?答案是函數指針,函數名永遠都只是一串無用的字符串。

某本書上的規則:函數名在用于非函數調用的時候,都等效于函數指針。

 

說到子進程只是一個額外的流程,那他跟父進程的聯系和區別是什么呢?

我很想建議你看看linux內核的注解(有興趣可以看看,那里才有本質上的了解),總之,fork后,子進程會復制父進程的task_struct結構,并為子進程的堆棧分配物理頁。理論上來說,子進程應該完整地復制父進程的堆,棧以及數據空間,但是2者共享正文段。

關于寫時復制:由于一般 fork后面都接著exec,所以,現在的 fork都在用寫時復制的技術,顧名思意,就是,數據段,堆,棧,一開始并不復制,由父,子進程共享,并將這些內存設置為只讀。直到父,子進程一方嘗試寫這些區域,則內核才為需要修改的那片內存拷貝副本。這樣做可以提高 fork的效率。

 

三.多線程

線程是可執行代碼的可分派單元。這個名稱來源于“執行的線索”的概念。在基于線程的多任務的環境中,所有進程有至少一個線程,但是它們可以具有多個任務。這意味著單個程序可以并發執行兩個或者多個任務。

 

簡而言之,線程就是把一個進程分為很多片,每一片都可以是一個獨立的流程。這已經明顯不同于多進程了,進程是一個拷貝的流程,而線程只是把一條河流截成很多條小溪。它沒有拷貝這些額外的開銷,但是僅僅是現存的一條河流,就被多線程技術幾乎無開銷地轉成很多條小流程,它的偉大就在于它少之又少的系統開銷。(當然偉大的后面又引發了重入性等種種問題,這個后面慢慢比較)。

還是先看linux提供的多線程的系統調用:

 

int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void),  void *restrict arg);

Returns: 0 if OK, error number on failure

第一個參數為指向線程標識符的指針。 第二個參數用來設置線程屬性。 第三個參數是線程運行函數的起始地址。 最后一個參數是運行函數的參數。

 

  1. #include<stdio.h>
  2. #include<string.h>
  3. #include<stdlib.h>
  4. #include<unistd.h>
  5. #include<pthread.h>
  6. void* task1(void*);
  7. void* task2(void*);
  8. void usr();
  9. int p1,p2;
  10. int main()
  11. {
  12. usr();
  13. getchar();
  14. return 1;
  15. }
  16. void usr()
  17. {
  18. pthread_t pid1, pid2;
  19. pthread_attr_t attr;
  20. void *p;
  21. int ret=0;
  22. pthread_attr_init(&attr); //初始化線程屬性結構
  23. pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); //設置attr結構為分離
  24. pthread_create(&pid1, &attr, task1, NULL); //創建線程,返回線程號給pid1,線程屬性設置為attr的屬性,線程函數入口為task1,參數為NULL
  25. pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
  26. pthread_create(&pid2, &attr, task2, NULL);
  27. //前臺工作
  28. ret=pthread_join(pid2, &p); //等待pid2返回,返回值賦給p
  29. printf("after pthread2:ret=%d,p=%d/n", ret,(int)p);
  30. }
  31. void* task1(void *arg1)
  32. {
  33. printf("task1/n");
  34. //艱苦而無法預料的工作,設置為分離線程,任其自生自滅
  35. pthread_exit( (void *)1);
  36. }
  37. void* task2(void *arg2)
  38. {
  39. int i=0;
  40. printf("thread2 begin./n");
  41. //繼續送外賣的工作
  42. pthread_exit((void *)2);
  43. }
  1. #include<stdio.h>
  2. #include<string.h>
  3. #include<stdlib.h>
  4. #include<unistd.h>
  5. #include<pthread.h>
  6. void* task1(void*);
  7. void* task2(void*);
  8. void usr();
  9. int p1,p2;
  10. int main()
  11. {
  12. usr();
  13. getchar();
  14. return 1;
  15. }
  16. void usr()
  17. {
  18. pthread_t pid1, pid2;
  19. pthread_attr_t attr;
  20. void *p;
  21. int ret=0;
  22. pthread_attr_init(&attr); //初始化線程屬性結構
  23. pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); //設置attr結構為分離
  24. pthread_create(&pid1, &attr, task1, NULL); //創建線程,返回線程號給pid1,線程屬性設置為attr的屬性,線程函數入口為task1,參數為NULL
  25. pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
  26. pthread_create(&pid2, &attr, task2, NULL);
  27. //前臺工作
  28. ret=pthread_join(pid2, &p); //等待pid2返回,返回值賦給p
  29. printf("after pthread2:ret=%d,p=%d/n", ret,(int)p);
  30. }
  31. void* task1(void *arg1)
  32. {
  33. printf("task1/n");
  34. //艱苦而無法預料的工作,設置為分離線程,任其自生自滅
  35. pthread_exit( (void *)1);
  36. }
  37. void* task2(void *arg2)
  38. {
  39. int i=0;
  40. printf("thread2 begin./n");
  41. //繼續送外賣的工作
  42. pthread_exit((void *)2);
  43. }

 

這個多線程的例子應該很明了了,主線程做自己的事情,生成2個子線程,task1為分離,任其自生自滅,而task2還是繼續送外賣,需要等待返回。(因該還記得前面說過僵尸進程吧,線程也是需要等待的。如果不想等待,就設置線程為分離線程)

額外的說下,linux下要編譯使用線程的代碼,一定要記得調用pthread庫。如下編譯:

gcc -o pthrea -pthread pthrea.c

 

四.比較以及注意事項

 

1.看完前面,應該對多進程和多線程有個直觀的認識。如果總結多進程和多線程的區別,你肯定能說,前者開銷大,后者開銷較小。確實,這就是最基本的區別。

2.線程函數的可重入性:

說到函數的可重入,和線程安全,我偷懶了,引用網上的一些總結。

 

線程安全:概念比較直觀。一般說來,一個函數被稱為線程安全的,當且僅當被多個并發線程反復調用時,它會一直產生正確的結果。

 

 

 

 

 

可重入:概念基本沒有比較正式的完整解釋,但是它比線程安全要求更嚴格。根據經驗,所謂“重入”,常見的情況是,程序執行到某個函數foo()時,收到信號,于是暫停目前正在執行的函數,轉到信號處理函數,而這個信號處理函數的執行過程中,又恰恰也會進入到剛剛執行的函數foo(),這樣便發生了所謂的重入。此時如果foo()能夠正確的運行,而且處理完成后,之前暫停的foo()也能夠正確運行,則說明它是可重入的。

線程安全的條件:

要確保函數線程安全,主要需要考慮的是線程之間的共享變量。屬于同一進程的不同線程會共享進程內存空間中的全局區和堆,而私有的線程空間則主要包括棧和寄存器。因此,對于同一進程的不同線程來說,每個線程的局部變量都是私有的,而全局變量、局部靜態變量、分配于堆的變量都是共享的。在對這些共享變量進行訪問時,如果要保證線程安全,則必須通過加鎖的方式。

可重入的判斷條件:

要確保函數可重入,需滿足一下幾個條件:

1、不在函數內部使用靜態或全局數據  2、不返回靜態或全局數據,所有數據都由函數的調用者提供。  3、使用本地數據,或者通過制作全局數據的本地拷貝來保護全局數據。 4、不調用不可重入函數。

 

可重入與線程安全并不等同,一般說來,可重入的函數一定是線程安全的,但反過來不一定成立。它們的關系可用下圖來表示:

 

 

比如:strtok函數是既不可重入的,也不是線程安全的;加鎖的strtok不是可重入的,但線程安全;而strtok_r既是可重入的,也是線程安全的。

 

如果我們的線程函數不是線程安全的,那在多線程調用的情況下,可能導致的后果是顯而易見的——共享變量的值由于不同線程的訪問,可能發生不可預料的變化,進而導致程序的錯誤,甚至崩潰。

 

3.關于ipC(進程間通信)

由于多進程要并發協調工作,進程間的同步,通信是在所難免的。

稍微列舉一下linux常見的IPC.

linux下進程間通信的幾種主要手段簡介:

  1. 管道(Pipe)及有名管道(named pipe):管道可用于具有親緣關系進程間的通信,有名管道克服了管道沒有名字的限制,因此,除具有管道所具有的功能外,它還允許無親緣關系進程間的通信;
  2. 信號(Signal):信號是比較復雜的通信方式,用于通知接受進程有某種事件發生,除了用于進程間通信外,進程還可以發送信號給進程本身;linux除了支持Unix早期信號語義函數sigal外,還支持語義符合Posix.1標準的信號函數sigaction(實際上,該函數是基于BSD的,BSD為了實現可靠信號機制,又能夠統一對外接口,用sigaction函數重新實現了signal函數);
  3. 報文(Message)隊列(消息隊列):消息隊列是消息的鏈接表,包括Posix消息隊列system V消息隊列。有足夠權限的進程可以向隊列中添加消息,被賦予讀權限的進程則可以讀走隊列中的消息。消息隊列克服了信號承載信息量少,管道只能承載無格式字節流以及緩沖區大小受限等缺點。
  4. 共享內存:使得多個進程可以訪問同一塊內存空間,是最快的可用IPC形式。是針對其他通信機制運行效率較低而設計的。往往與其它通信機制,如信號量結合使用,來達到進程間的同步及互斥。
  5. 信號量(semaphore):主要作為進程間以及同一進程不同線程之間的同步手段。
  6. 套接口(Socket):更為一般的進程間通信機制,可用于不同機器之間的進程間通信。起初是由Unix系統的BSD分支開發出來的,但現在一般可以移植到其它類Unix系統上:Linux和System V的變種都支持套接字。

或許你會有疑問,那多線程間要通信,應該怎么做?前面已經說了,多數的多線程都是在同一個進程下的,它們共享該進程的全局變量,我們可以通過全局變量來實現線程間通信。如果是不同的進程下的2個線程間通信,直接參考進程間通信。

 

4.關于線程的堆棧

說一下線程自己的堆棧問題。

是的,生成子線程后,它會獲取一部分該進程的堆??臻g,作為其名義上的獨立的私有空間。(為何是名義上的呢?)由于,這些線程屬于同一個進程,其他線程只要獲取了你私有堆棧上某些數據的指針,其他線程便可以自由訪問你的名義上的私有空間上的數據變量。(注:而多進程是不可以的,因為不同的進程,相同的虛擬地址,基本不可能映射到相同的物理地址)

 

 

5.在子線程里fork

 

看過好幾次有人問,在子線程函數里調用system或者 fork為何出錯,或者fork產生的子進程是完全復制父進程的嗎?

我測試過,只要你的線程函數滿足前面的要求,都是正常的。

 

  1. #include<stdio.h>
  2. #include<string.h>
  3. #include<stdlib.h>
  4. #include<unistd.h>
  5. #include<pthread.h>
  6. void* task1(void *arg1)
  7. {
  8. printf("task1/n");
  9. system("ls");
  10. pthread_exit( (void *)1);
  11. }
  12. int main()
  13. {
  14. int ret=0;
  15. void *p;
  16. int p1=0;
  17. pthread_t pid1;
  18. pthread_create(&pid1, NULL, task1, NULL);
  19. ret=pthread_join(pid1, &p);
  20. printf("end main/n");
  21. return 1;
  22. }
  1. #include<stdio.h>
  2. #include<string.h>
  3. #include<stdlib.h>
  4. #include<unistd.h>
  5. #include<pthread.h>
  6. void* task1(void *arg1)
  7. {
  8. printf("task1/n");
  9. system("ls");
  10. pthread_exit( (void *)1);
  11. }
  12. int main()
  13. {
  14. int ret=0;
  15. void *p;
  16. int p1=0;
  17. pthread_t pid1;
  18. pthread_create(&pid1, NULL, task1, NULL);
  19. ret=pthread_join(pid1, &p);
  20. printf("end main/n");
  21. return 1;
  22. }

 

 

上面這段代碼就可以正常得調用ls指令。

 

不過,在同時調用多進程(子進程里也調用線程函數)和多線程的情況下,函數體內很有可能死鎖。

具體的例子可以看看這篇文章。

 

http://www.cppblog.com/lymons/archive/2008/06/01/51836.aspx


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日韩va亚洲va欧洲va国产| 91日韩在线播放| 91超碰caoporn97人人| 最近2019年好看中文字幕视频| 日韩免费观看高清| 欧美性视频网站| 热99精品只有里视频精品| 久国内精品在线| 久久精品国亚洲| 久久亚洲国产精品成人av秋霞| 国产精品久久久久一区二区| 欧美丰满少妇xxxx| 福利精品视频在线| 国产精品黄页免费高清在线观看| 欧美另类99xxxxx| 欧美日韩性视频在线| 日本久久久久久久久| 日本成人精品在线| 欧美性极品少妇精品网站| 亚洲伊人一本大道中文字幕| 久久精品福利视频| 精品久久中文字幕| 精品国产乱码久久久久久天美| 国语自产精品视频在免费| 亚洲二区中文字幕| 精品中文字幕久久久久久| 亚洲精品永久免费| 国产精品色悠悠| 成人性生交大片免费看小说| 欧美日韩国产中文精品字幕自在自线| 午夜精品久久久久久久白皮肤| 久久久国产精彩视频美女艺术照福利| 欧美激情网站在线观看| 国产成人精品午夜| 亚洲国产精品久久91精品| 91久久国产综合久久91精品网站| 欧美精品少妇videofree| 亚洲午夜未满十八勿入免费观看全集| 国产精品人成电影在线观看| 国产91精品高潮白浆喷水| 欧美性理论片在线观看片免费| 人妖精品videosex性欧美| 综合网日日天干夜夜久久| 久久精品视频在线播放| 欧美日本国产在线| 亚洲国产欧美一区二区丝袜黑人| 亚洲欧美精品在线| 欧美激情精品久久久久久变态| 成人欧美一区二区三区在线湿哒哒| 国产亚洲精品久久久优势| 欧美色另类天堂2015| 青青a在线精品免费观看| 日韩在线观看免费网站| 欧美视频精品一区| 国产精品主播视频| 5566日本婷婷色中文字幕97| 亚洲区中文字幕| 精品久久久国产精品999| 国产色视频一区| 亚洲国产成人精品一区二区| 秋霞成人午夜鲁丝一区二区三区| 欧美电影免费在线观看| 欧美在线一区二区三区四| 色噜噜亚洲精品中文字幕| 国产综合在线看| 亚洲片在线观看| 亚洲欧美在线第一页| 1769国内精品视频在线播放| 亚洲成色www8888| 亚洲欧美日韩久久久久久| 亚洲精品视频网上网址在线观看| 国产小视频国产精品| 欧美日产国产成人免费图片| 欧美激情乱人伦一区| 亚洲va欧美va在线观看| 久久久久日韩精品久久久男男| 精品久久久国产| 日韩av网站导航| 欧美交受高潮1| 91精品视频大全| 日本精品在线视频| 91免费在线视频网站| 国产999精品视频| 性欧美xxxx| 日韩视频在线观看免费| 久久99热精品| 欧美精品制服第一页| 激情久久av一区av二区av三区| www.xxxx精品| 久久久999精品| 国产精品美乳一区二区免费| 久久久电影免费观看完整版| 91九色视频在线| 日韩精品在线观看一区二区| 国产成人av在线播放| 色综合久久悠悠| 亚洲精品国精品久久99热一| 国产精品老女人视频| 国产一区二区免费| 国产精品白丝av嫩草影院| 成人国产精品日本在线| 日韩中文字幕视频在线| 91日本在线视频| 亚洲精品自拍视频| 中文字幕亚洲字幕| 久久91精品国产91久久久| 国模吧一区二区| 欧美日韩国产中文精品字幕自在自线| 久久久久久久久久国产精品| 大桥未久av一区二区三区| 欧美日韩电影在线观看| 欧美视频一二三| 91久久国产精品91久久性色| 欧美精品在线免费| 欧美一级电影免费在线观看| 一本一本久久a久久精品综合小说| 国产有码一区二区| 亚洲男人天堂2019| 国产成人久久久| 国产91精品网站| 久久久久久一区二区三区| 欧美黑人又粗大| 亚洲sss综合天堂久久| 欧美一级片久久久久久久| 久久精品国产欧美亚洲人人爽| 国产精品久久视频| 亚洲精品www| 亚洲精品在线观看www| 国产一区二区成人| 色偷偷偷亚洲综合网另类| 日韩欧美极品在线观看| 欧美日韩中文字幕日韩欧美| 91香蕉国产在线观看| 中文字幕欧美日韩| 按摩亚洲人久久| 亚洲精品成人久久| 8090理伦午夜在线电影| 国产精品日韩在线观看| 一夜七次郎国产精品亚洲| 91在线国产电影| 大胆欧美人体视频| 国产在线精品播放| 亚洲第一区中文99精品| 国产视频久久久久久久| 国产精品午夜视频| 亚洲国产美女久久久久| 成人av在线网址| 成人夜晚看av| 97高清免费视频| 亚洲理论片在线观看| 日本国产一区二区三区| 在线观看国产精品淫| 555www成人网| 日韩中文字幕欧美| 亚洲成人av片在线观看| 欧美激情精品久久久| 国产精品一区二区三区成人| 久久成人这里只有精品| 91成人在线播放| 久久全国免费视频| 欧美二区乱c黑人| 亚洲一品av免费观看| 精品国产鲁一鲁一区二区张丽| 全亚洲最色的网站在线观看|