C語言執行緒安全問題
阿新 • • 發佈:2021-06-28
執行緒安全問題
#include <stdio.h> #include <tinycthread.h> #include <io_utils.h> int count = 0; int Counter(void*arg) { for(int i = 0;i<100000;i++) { count++; /* * int temp = count; * count=temp+1; * return temp; * */ } return 0; } int main() { thrd_t t1; thrd_t t2; thrd_create(&t1,Counter,NULL); thrd_create(&t2,Counter,NULL); thrd_join(t1,NULL); thrd_join(t2,NULL); PRINT_INT(count); return 0; }
- 執行結果不是所要值原因是count++在併發時產生衝突
執行緒安全的產生
- 對共享資源進行非原子的訪問
- 不同執行緒之間程式碼可見性問題
- 執行緒內部程式碼編譯時的重排序問題
解決方法一 消除副作用
#include <stdio.h> #include <tinycthread.h> #include <io_utils.h> int Counter(void*arg) { int count = 0; for(int i = 0;i<100000;i++) { count++; } return count; } int main() { thrd_t t1; thrd_t t2; thrd_create(&t1,Counter,NULL); thrd_create(&t2,Counter,NULL); int count = 0; int result = 0; thrd_join(t1,&result); count+=result; thrd_join(t2,&result); count+=result; PRINT_INT(count); return 0; }
解決方法二 原子型別
#include<stdio.h> #include <tinycthread.h> #include <io_utils.h> #include <stdatomic.h> atomic_int count = 0; //設定原子型別 int Counter(void*arg) { for(int i = 0;i<100000;i++) { count++; } return 0; } int main() { thrd_t t1; thrd_t t2; thrd_create(&t1,Counter,NULL); thrd_create(&t2,Counter,NULL); thrd_join(t1,NULL); thrd_join(t2,NULL); PRINT_INT(count); return 0; }
解決方法三 原子操作
#include<stdio.h>
#include <tinycthread.h>
#include <io_utils.h>
#include <stdatomic.h>
atomic_flag resume_flag = ATOMIC_FLAG_INIT;
int PrintNumber(void*arg)
{
int current = 0;
while(atomic_flag_test_and_set(&resume_flag))
{
current++;
PRINT_INT(current);
thrd_sleep(&(struct timespec){.tv_sec=1},NULL);
}
return current;
}
int main()
{
atomic_flag_test_and_set(&resume_flag);
thrd_t t;
thrd_create(&t,PrintNumber,NULL);
thrd_sleep(&(struct timespec){.tv_sec=5},NULL);
atomic_flag_clear(&resume_flag);
int last_number = 0;
thrd_join(t,&last_number);
PRINT_INT(last_number);
return 0;
}
解決方法四 鎖
#include <stdio.h>
#include <tinycthread.h>
#include <io_utils.h>
int count = 0;
mtx_t mutex;
int Counter(void*arg)
{
for(int i = 0;i<100000;i++)
{
mtx_lock(&mutex);
count++;
mtx_unlock(&mutex);
/*
* int temp = count;
* count=temp+1;
* return temp;
* */
}
return 0;
}
int main()
{
mtx_init(&mutex,mtx_plain);
thrd_t t1;
thrd_t t2;
thrd_create(&t1,Counter,NULL);
thrd_create(&t2,Counter,NULL);
thrd_join(t1,NULL);
thrd_join(t2,NULL);
PRINT_INT(count);
mtx_destroy(&mutex);
return 0;
}
解決方法五 執行緒儲存期
#include <stdio.h>
#include <tinycthread.h>
#include <io_utils.h>
_Thread_local int count = 0;//每個執行緒都有一個獨立的副本
int Counter(int* arg)
{
for(int i = 0;i<100000;i++)
{
count+=*arg;
/*
* int temp = count;
* count=temp+1;
* return temp;
* */
}
PRINT_INT(count);
return 0;
}
int main()
{
thrd_t t1;
thrd_t t2;
int arg_1 = 1;
int arg_2 = 2;
thrd_create(&t1,Counter,&arg_1);
thrd_create(&t2,Counter,&arg_2);
thrd_join(t1,NULL);
thrd_join(t2,NULL);
PRINT_INT(count);
return 0;
//count: 100000
//count: 200000
//count: 0
}
解決方法六 tss
#include <stdio.h>
#include <tinycthread.h>
#include <io_utils.h>
tss_t count_key;
void MyFree(void*ptr)
{
PRINTLNF("free %#x",ptr);
free(ptr);
}
int Counter(int* arg)
{
int* count = malloc(sizeof(int));
*count = 0;
if(tss_set(count_key,count) == thrd_success) //如果繫結成功
{
for (int i = 0; i < 100000; i++) {
*count += *arg;
/*
* int temp = count;
* count=temp+1;
* return temp;
* */
}
}
PRINT_INT(*count);
PRINT_INT(*((int*)tss_get(count_key)));
return 0;
}
int main()
{
if(tss_create(&count_key,MyFree)==thrd_success)
{
thrd_t t1;
thrd_t t2;
int arg_1 = 1;
int arg_2 = 2;
thrd_create(&t1,Counter,&arg_1);
thrd_create(&t2,Counter,&arg_2);
//tss_delete(count_key); 如果線上程結束前刪除,則不會呼叫MyFree,需要自己手動釋放記憶體。
thrd_join(t1,NULL);
thrd_join(t2,NULL);
puts("t_1,t_2 ends");
tss_delete(count_key);
PRINTLNF("count_key delete");
}
return 0;
}