嵌入式入門學習筆記,遇到的問題以及心得體會!
DAY28
概述:
一、執行緒
二、同步和互斥問題
三、如何實作同步
四、如何實作互斥
筆記:
一、執行緒
1、什么是執行緒:
(1)執行緒是輕量級的行程
(2)執行緒存在于行程內,不能獨立存在
(3)執行緒參與CPU調度,行程是系統資源分配最小單位,執行緒是系統調度的最小單位
(4)在單核CPU中,多執行緒并發屬于偽并發,但是不牽扯虛擬地址空間的切換,所以開銷比行程間切換要小很多
(5)在多核CPU中,多執行緒可以實作真并發
前面我們已經提到,行程是系統中程式執行和資源分配的基本單位每個行程都擁有自己的資料段,代碼段和堆
堆疊段,這就造成了行程在進行切換時,作業系統的開銷比較大,
為了提高效率,作業系統又引入了另一個概念——執行緒,也稱輕量級的行程,執行緒可以對行程的記憶體空間和資源進行訪問,并與同一行程中的其他執行緒共享,因此,執行緒的背景關系切換的開銷比行程小得多,
一個行程可以擁有多個執行緒,其中每一個執行緒共享該行程所擁有的資源,要注意的是,由于執行緒共享了行程的資源和地址空間,因此,任何執行緒對于系統資源的操作都會給其他執行緒帶來影響,由此可知,多執行緒中的同步是非常重要的問題,在多執行緒系統中,行程與執行緒的關系圖如下:

2、哪些是多執行緒共享的資源,哪些是執行緒私有的資源:
執行緒(Thread):有時候被稱為輕量級行程(Lightweight Process, LWP),是程式執行流的最小單元,一個標準的執行緒由執行緒ID、當前指令指標(PC)、暫存器集合和堆疊組成,
通常一個行程由一個到多個執行緒組成,各個執行緒之間共享程式的記憶體空間(包括代碼段、資料段、堆等)以及一些進成級的資源(如打開檔案和信號),一個經典的執行緒與行程的關系圖如下:
|---------------------------------------------------|
|代碼 |資料 |行程空間 |打開檔案 |
|---------------------------------------------------|
| |
| |-----------| |-----------| |-----------| |
| |暫存器 | |暫存器 | |暫存器 | |
| |-----------| |-----------| |-----------| |
| |堆疊 | |堆疊 | |堆疊 | |
| |-----------| |-----------| |-----------| |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| |MainThread | |Thread1 | |Thread2 | |
| |-----------| |-----------| |-----------| |
| |
| 行程的地址空間使用分配圖 |
|---------------------------------------------------|
3、使用多執行緒的原因:
<1>某個操作可能會陷入長時間等待,等待的執行緒會進入睡眠狀態,無法繼續執行,多執行緒執行可以有效利用等待的時間,典型的例子是等待網路回應,這可能要花費數秒甚至數十秒,
<2>某個操作(常常是計算)會消耗大量的時間,如果只有一個執行緒,程式和用戶之間的互動會中斷,多執行緒可以讓一個執行緒負責互動,另一個執行緒負責計算,
<3>程式邏輯本身要求并發操作,例如一個多端下載軟體(Bittorrent)
<4>多CPU或多核計算機,本身具備同時執行多個執行緒的能力,因此單執行緒程式無法全面地發揮計算機地全部計算能力,
<5>相對于多行程應用程式,多執行緒在資料共享方面效率要高很多,
4、執行緒地訪問權限:
執行緒地訪問非常自由,它可以訪問行程記憶體里的所有資料,甚至包括其他執行緒的堆疊(如果它知道其他執行緒的堆疊地址,那么這就是很少見的情況),但實際運用中執行緒也擁有自己的私有存盤空間,包括以下幾個方面:
<1>堆疊:盡管并非完全無法被其他執行緒訪問,但一般情況下仍然可以認為是私有的,
<2>執行緒區域存盤(Thread Local Storage, TLS):執行緒區域存盤是某些作業系統為執行緒單獨提供的私有空間,但通常只具有很有限的容量,
<3>暫存器(包括PC暫存器):暫存器是執行流的基本資料,因此為執行緒私有,
從C程式員的角度來分析,資料在執行緒之間是否私有如下表:
|-----------------------|-----------------------------------------------|
|執行緒私有 |執行緒之間共享(行程所有) |
|-----------------------|-----------------------------------------------|
|區域變數 |全域變數 |
|函式的引數 |堆上的資料 |
|TLS資料 |函式里的靜態變數 |
| |程式代碼,任何執行緒都有權利讀取并執行任何代碼 |
| |打開的檔案,A執行緒打開的檔案可以由B執行緒讀寫 |
|-----------------------|-----------------------------------------------|

5、如何創建執行緒:
pthread_create()
頭檔案:
#include <pthread.h>
/*
*函式名:pthread_create
*函式功能:創建一個執行緒
*函式引數:
* pthread_t *thread:
* const pthread_attr_t *attr:創建執行緒時的引數的指標
* void *(*start_routine) (void *):執行緒處理函式的指標
* void *arg:執行緒處理函式的引數
*函式回傳值:int:成功回傳0,失敗回傳非零
*/
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
注意:Compile and link with -pthread. 編譯時加-lpthread
6、如何讓執行緒執行特定任務:
通過執行緒處理函式來實作
7、執行緒的釋放:
(1)pthread_detach
頭檔案:
#include <pthread.h>
/*
*函式名:pthread_detach
*函式功能:銷毀一個執行緒
*函式的引數:
* pthread_t thread:要銷毀的執行緒
*函式回傳值:int:成功反回0,失敗錯誤碼
*/
int pthread_detach(pthread_t thread);
(2)pthread_join
頭檔案:
#include <pthread.h>
/*
*函式名:pthread_join
*函式功能:等待執行緒的退出
*函式引數:
* pthread_t thread:要等待的執行緒
* void **retval:執行緒的退出狀態
*函式回傳值:int:成功反回0,失敗錯誤碼
*/
int pthread_join(pthread_t thread, void **retval);
(3)pthread_exit
(4)pthread_cancel
8.執行緒代碼演示
#include <stdio.h>
#include <pthread.h>
void *t1f(void *arg)
{
while(1)
{
printf("Tom and Jerry!\n");
}
return NULL;
}
void *t2f(void *arg)
{
while(1)
{
printf("Tomats\n");
}
return NULL;
}
int main()
{
//create thread two
pthread_t tid1, tid2;
int ret = pthread_create(&tid1, NULL, t1f ,NULL);
if(ret != 0)
{
puts("create thread 1 error.");
return -1;
}
ret = pthread_create(&tid2, NULL, t2f ,NULL);
if(ret != 0)
{
puts("create thread 1 error.");
return -1;
}
pthread_detach(tid1);
pthread_detach(tid2);
//detach thread
while(1)
{};
return 0;
}
二、同步和互斥問題
1.什么是臨界資源
并發編程中,多執行緒/行程 共享的資源,都叫臨界資源
2.什么是臨界區
代碼中操作臨界資源的部分,稱為臨界區
3.操作臨界資源的使用要注意哪些問題
讀讀不互斥
讀寫互斥
寫寫互斥
4.如何安全的操作臨界資源
同步
互斥
三、同步
1.什么是同步:
多行程/多執行緒在訪問臨界資源時,按照一定的操作順序來訪問,
2.什么時候使用同步
需要按照一定的順序訪問臨界資源時使用
3.實作同步的方法
1.信號量
有名信號量:行程使用
無名信號量:執行緒使用
2.能夠共享的變數
(1)行程:
管道
------------有名管道
------------無名管道
共享記憶體
訊息佇列
(2) 執行緒:
全域變數
靜態變數
4.如何實作無名信號量
(1)sem_init
/*需要包含的頭檔案*/
#include <semaphore.h>
/*
*函式名:sem_init
*函式功能:初始化一個無名信號量
*函式引數:
* sem_t *sem:信號量操作物件的指標
* int pshare:默認為0為執行緒使用
* unsigned int value:信號量的初始值
*函式回傳值:int:成功回傳0,失敗回傳-1
*/
int sem_init(sem_t *sem, int pshare, unsigned int value);
(2)sem_wait
(3)sem_post
(4)sem_destroy
/*需要包含的頭檔案*/
#include <semaphore.h>
/*
*函式名:sem_destroy
*函式功能:銷毀一個無名信號量
*函式引數:sem_t *sem:被銷毀的無名信號量的操作物件指標
*函式回傳值:int :成功0,失敗-1
*/
int sem_destroy(sem_t *sem);
5.如何使用無名信號量
多執行緒需要同步訪問臨界資源時使用
6.同步代碼演示
(1)flags
create_thread-------------------------------------------------------------------------------------------------代碼如下
#include <stdio.h>
#include <pthread.h>
int c;
int flag;
void *t1f(void *arg)
{
while(1)
{
if(flag == 0)
{
if(c < 100)
{
c++;
}
else
{
c = 0;
}
flag = 1;
}
}
return NULL;
}
void *t2f(void *arg)
{
while(1)
{
if(flag == 1)
{
printf("c:%d\n",c);
flag = 0;
}
}
return NULL;
}
int main()
{
//create thread two
pthread_t tid1, tid2;
int ret = pthread_create(&tid1, NULL, t1f ,NULL);
if(ret != 0)
{
puts("create thread 1 error.");
return -1;
}
ret = pthread_create(&tid2, NULL, t2f ,NULL);
if(ret != 0)
{
puts("create thread 1 error.");
return -1;
}
pthread_detach(tid1);
pthread_detach(tid2);
//detach thread
while(1)
{};
return 0;
}
(2)unamed_sem
create_thread-------------------------------------------------------------------------------------------------代碼如下
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
int c;
sem_t mysem;
void *t1f(void *arg)
{
while(1)
{
int sval = 0;
sem_getvalue(&mysem, &sval);
if(sval == 2)
{
sem_wait(&mysem);
if(c < 100)
{
c++;
}
else
{
c = 0;
}
}
}
return NULL;
}
void *t2f(void *arg)
{
while(1)
{
int sval = 0;
sem_getvalue(&mysem, &sval);
if(sval == 1)
{
sem_wait(&mysem);
printf("c:%d\n",c);
sem_post(&mysem);
sem_post(&mysem);
}
}
return NULL;
}
int main()
{
//sem_init
int ret = sem_init(&mysem,0,2);
if(ret != 0)
{
puts("sem_init error.");
return -1;
}
//create thread two
pthread_t tid1, tid2;
ret = pthread_create(&tid1, NULL, t1f ,NULL);
if(ret != 0)
{
puts("create thread 1 error.");
return -1;
}
ret = pthread_create(&tid2, NULL, t2f ,NULL);
if(ret != 0)
{
puts("create thread 1 error.");
return -1;
}
pthread_detach(tid1);
pthread_detach(tid2);
//detach thread
//sem_destroy
sem_destroy(&mysem);
while(1)
{};
return 0;
}
四、互斥
1.什么是互斥
多執行緒/多行程訪問臨界資源時,沒有順序要求,只需要滿足寫寫互斥和讀寫互斥時,此時說的就是不能同時訪問臨界資源,
2.什么時候使用互斥
寫寫互斥,讀寫互斥
3.如何實作互斥鎖
<1>初始化互斥鎖
頭檔案:
#include <pthread.h>
/*
*函式名:pthread_mutex_init
*函式功能:初始化互斥鎖
*函式引數:
* pthread_mutex_t *mutex:鎖的指標
* pthread_mutexattr_t *attr:鎖的引數的指標,通常為NULL
*函式回傳值:成功為0,失敗回傳-1
*/
int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t *attr);
<2>加鎖
頭檔案:#include <pthread.h>
/*
*函式名:pthread_mutex_lock
*函式功能:申請鎖資源
*函式引數:pthread_mutex_t *mutex:鎖的指標
*函式回傳值:成功回傳0,失敗-1
*/
int pthread_mutex_lock(pthread_mutex_t *mutex);
<3>解鎖
頭檔案:
#include <pthread.h>
/*
*函式名:pthread_mutex_unlock
*函式功能:釋放互斥鎖
*函式引數:pthread_mutex_t *mutex:鎖的指標
*函式回傳值:成功回傳0,失敗-1
*/
int pthread_mutex_unlock(pthread_mutex_t *mutex);
4.互斥代碼演示:
create_thread-------------------------------------------------------------------------------------------------代碼如下
#include <stdio.h>
#include <string.h>
#include <pthread.h>
char buf[100];
pthread_mutex_t mutex;
void *t1f(void *arg)
{
while(1)
{
pthread_mutex_lock(&mutex);
puts(buf);
memset(buf,0,100);
strcpy(buf,"Jerry is a student.");
pthread_mutex_unlock(&mutex);
}
return NULL;
}
void *t2f(void *arg)
{
while(1)
{
pthread_mutex_lock(&mutex);
puts(buf);
memset(buf,0,100);
strcpy(buf,"Ben is a teacher.");
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main()
{
pthread_mutex_init(&mutex, NULL);
//create thread two
pthread_t tid1, tid2;
int ret = pthread_create(&tid1, NULL, t1f ,NULL);
if(ret != 0)
{
puts("create thread 1 error.");
return -1;
}
ret = pthread_create(&tid2, NULL, t2f ,NULL);
if(ret != 0)
{
puts("create thread 1 error.");
return -1;
}
pthread_detach(tid1);
pthread_detach(tid2);
//detach thread
pthread_mutex_destroy(&mutex);
while(1)
{};
return 0;
}
5.注意:
互斥鎖只能由加鎖的執行緒解鎖
當臨界資源加鎖后沒有解鎖,其他執行緒/行程無法訪問臨界資源時,就會產生死鎖,
附加:并發編程中經典問題:
1.哲學家共餐問題
2.生產者消費者問題
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/271935.html
標籤:其他
上一篇:Python 操作HDFS
