多线程竞争

男娘i 2022-08-04 08:42 257阅读 0赞
  1. 在多线程编程中,会经常碰到资源竞争的情况,如果多个线程同时访问同个资源,会照成未知的错误。

如以下实例代码,多个线程对同个全局变量进行加1操作,得到的结果并非是我们想要的结果:

  1. unsigned long g_count_num = 0;
  2. long long getSystemTime() {
  3. struct timeb t;
  4. ftime(&t);
  5. return 1000 * t.time + t.millitm;
  6. }
  7. void * pth_test_fun(void * a_arg)
  8. {
  9. int b_loop;
  10. for(b_loop = 0; b_loop < 10000000; b_loop++)
  11. {
  12. g_count_num++;
  13. }
  14. return 0;
  15. }
  16. int main()
  17. {
  18. int b_loop;
  19. long long b_begin, b_end;
  20. pthread_t b_pth_id[20];
  21. b_begin = getSystemTime();
  22. for(b_loop = 0; b_loop < 10; b_loop++)
  23. {
  24. pthread_create(&b_pth_id[b_loop], NULL, pth_test_fun, NULL);
  25. }
  26. for(b_loop = 0; b_loop < 10; b_loop++)
  27. {
  28. pthread_join(b_pth_id[b_loop], NULL);
  29. }
  30. b_end = getSystemTime();
  31. printf("time = %lu, val = %lu\n", b_end - b_begin, g_count_num);
  32. return 0;
  33. }

运行结果:

Center

time是整个线程运行的总时间,val是全局变量g_count_num 的值,多个线程同时对该变量进行操作,当一个线程对该变量进行操作时,但还没对该变量进行加1操作,而另一个线程同时对该变量进行操作,读出的值是原值还没加1的值,这样就造成了最后变量g_count_num的值不正确

多线程操作同一个资源,最好加锁,给资源加上锁,保证同一个时刻只有一个线程可以访问该资源,其他访问该资源的线程阻塞直到访问该资源的线程解锁释放该资源。

最常见的就是利用pthread_mutex_lock进行加锁,如以下实例代码:

  1. unsigned long g_count_num = 0;
  2. pthread_mutex_t * g_pth_mutex = NULL;
  3. long long getSystemTime() {
  4. struct timeb t;
  5. ftime(&t);
  6. return 1000 * t.time + t.millitm;
  7. }
  8. void * pth_test_fun(void * a_arg)
  9. {
  10. int b_loop;
  11. for(b_loop = 0; b_loop < 10000000; b_loop++)
  12. {
  13. pthread_mutex_lock(g_pth_mutex);
  14. g_count_num++;
  15. pthread_mutex_unlock(g_pth_mutex);
  16. }
  17. return 0;
  18. }
  19. int main()
  20. {
  21. int b_loop;
  22. long long b_begin, b_end;
  23. pthread_t b_pth_id[20];
  24. pthread_mutex_init(g_pth_mutex, NULL);
  25. b_begin = getSystemTime();
  26. for(b_loop = 0; b_loop < 10; b_loop++)
  27. {
  28. pthread_create(&b_pth_id[b_loop], NULL, pth_test_fun, NULL);
  29. }
  30. for(b_loop = 0; b_loop < 10; b_loop++)
  31. {
  32. pthread_join(b_pth_id[b_loop], NULL);
  33. }
  34. b_end = getSystemTime();
  35. pthread_mutex_destroy(g_pth_mutex);
  36. printf("time = %lu, val = %lu\n", b_end - b_begin, g_count_num);
  37. return 0;
  38. }

运行结果:

Center 1
对资源加锁后,变量的值正常了。

除了pthread_mutex_lock可以对资源加锁外,我们还可以利用信号量,信号量就是一个计数器,计数可以访问资源的线程数,来试试信号量和锁那种性能高:

  1. unsigned long g_count_num = 0;
  2. int g_semphore_id = 0;
  3. #define SEM_KEY 10008
  4. long long getSystemTime() {
  5. struct timeb t;
  6. ftime(&t);
  7. return 1000 * t.time + t.millitm;
  8. }
  9. void * pth_test_fun(void * a_arg)
  10. {
  11. int b_loop;
  12. struct sembuf b_lock_sop = {0}, b_unlock_sop = {0};
  13. b_lock_sop.sem_num = 0;
  14. b_lock_sop.sem_op = -1;
  15. b_lock_sop.sem_flg = SEM_UNDO;
  16. b_unlock_sop.sem_num = 0;
  17. b_unlock_sop.sem_op = 1;
  18. b_unlock_sop.sem_flg = SEM_UNDO;
  19. for(b_loop = 0; b_loop < 10000000; b_loop++)
  20. {
  21. semop(g_semphore_id, &b_lock_sop, 1);
  22. g_count_num++;
  23. semop(g_semphore_id, &b_unlock_sop, 1);
  24. }
  25. return 0;
  26. }
  27. int main()
  28. {
  29. int b_loop;
  30. long long b_begin, b_end;
  31. pthread_t b_pth_id[20];
  32. union semun b_sem_val = {0};
  33. g_semphore_id = semget(SEM_KEY, 1, IPC_CREAT|0666);
  34. semctl(g_semphore_id, 0, SETVAL, 1);
  35. b_begin = getSystemTime();
  36. for(b_loop = 0; b_loop < 10; b_loop++)
  37. {
  38. pthread_create(&b_pth_id[b_loop], NULL, pth_test_fun, NULL);
  39. }
  40. for(b_loop = 0; b_loop < 10; b_loop++)
  41. {
  42. pthread_join(b_pth_id[b_loop], NULL);
  43. }
  44. b_end = getSystemTime();
  45. semctl(g_semphore_id, 0, IPC_RMID);
  46. printf("time = %lu, val = %lu\n", b_end - b_begin, g_count_num);
  47. return 0;
  48. }

运行结果:

Center 2

和上个程序对比,信号量的消耗时间比线程锁消耗的时间更多,在锁资源方面,线程锁性能高点

测试下一段代码前,先介绍一个概念,原子操作。

原子操作就是该操作不会被任何打断直至操作完成,操作系统定义了一些相关原子操作,比如对变量进行原子加1,原子减1.以下代码就是利用了系统原子操作特性atomic64_inc(原子加1操作),来实现多线程对同个资源的操作:

  1. typedef struct {
  2. volatile long counter;
  3. } atomic64_t;
  4. atomic64_t g_count_num = {0};
  5. static inline void atomic64_inc(atomic64_t *v)
  6. {
  7. asm volatile("lock ;" "incq %0"
  8. : "=m" (v->counter)
  9. : "m" (v->counter));
  10. }
  11. void * pth_test_fun(void * a_arg)
  12. {
  13. int b_loop;
  14. for(b_loop = 0; b_loop < 10000000; b_loop++)
  15. {
  16. atomic64_inc(&g_count_num);
  17. }
  18. return 0;
  19. }
  20. long long getSystemTime() {
  21. struct timeb t;
  22. ftime(&t);
  23. return 1000 * t.time + t.millitm;
  24. }
  25. int main()
  26. {
  27. int b_loop;
  28. long long b_begin, b_end;
  29. pthread_t b_pth_id[20];
  30. b_begin = getSystemTime();
  31. for(b_loop = 0; b_loop < 10; b_loop++)
  32. {
  33. pthread_create(&b_pth_id[b_loop], NULL, pth_test_fun, NULL);
  34. }
  35. for(b_loop = 0; b_loop < 10; b_loop++)
  36. {
  37. pthread_join(b_pth_id[b_loop], NULL);
  38. }
  39. b_end = getSystemTime();
  40. printf("time = %lu, val = %lu\n", b_end - b_begin, g_count_num.counter);
  41. return 0;
  42. }

运行结果:

Center 3

通过运行结果可看出,性能比加锁高出很多,因为毕竟加锁操作的实现也是经过原子操作实现的

再来看看另一段代码,利用__sync_fetch_and_add原子加1.

  1. unsigned long g_count_num = 0;
  2. void * pth_test_fun(void * a_arg)
  3. {
  4. int b_loop;
  5. for(b_loop = 0; b_loop < 10000000; b_loop++)
  6. {
  7. __sync_fetch_and_add(&g_count_num, 1);
  8. }
  9. return 0;
  10. }
  11. int main()
  12. {
  13. int b_loop;
  14. long long b_begin, b_end;
  15. pthread_t b_pth_id[20];
  16. b_begin = getSystemTime();
  17. for(b_loop = 0; b_loop < 10; b_loop++)
  18. {
  19. pthread_create(&b_pth_id[b_loop], NULL, pth_test_fun, NULL);
  20. }
  21. for(b_loop = 0; b_loop < 10; b_loop++)
  22. {
  23. pthread_join(b_pth_id[b_loop], NULL);
  24. }
  25. b_end = getSystemTime();
  26. printf("time = %lu, val = %lu\n", b_end - b_begin, g_count_num);
  27. return 0;
  28. }

运行结果:

Center 4

发表评论

表情:
评论列表 (有 0 条评论,257人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Java线:资源竞争示例

    在Java多线程中,资源竞争是一个常见的问题。简单来说,就是多个线程同时争夺有限的资源,如果没有适当的同步机制,可能会导致数据不一致或者死锁等问题。 以下是一个经典的资源共享

    相关 线之间的竞争

          进行多线程编程,同步控制是非常重要的,而同步控制就涉及到了锁。        对代码进行同步控制我们可以选择同步方法,也可以选择同步块,这两种方式各有优缺点,至于

    相关 线竞争

             在多线程编程中,会经常碰到资源竞争的情况,如果多个线程同时访问同个资源,会照成未知的错误。 如以下实例代码,多个线程对同个全局变量进行加1操作,得到的结果并

    相关 Java线--竞争条件

    竞争条件: Java多线程中多个线程对同一数据进行存取操作,可能出现某一线程尚未完成对该数据的操作,而另一线程又对该数据进行操作,造成了数据错误。 竞争条件产生的原因是