1 实验目的

探索、理解并掌握操作系统同步机制的设计和实现机理, 针对所谓的银行账户转账同步问题,构建基于 Peterson 算法的同步解决方案以及基于 Windows(或 Linux) 操作系统同步机制(主要是互斥机制) 的解决方案,并进行分析和比较。


2 实验内容

针对所谓的银行账户转账同步问题, 分析、设计和利用 C 语言编程实现基于 Peterson 算法的同步解决方案,以及基于 Windows(或 Linux)操作系统同步机制的相应解决方案,并就自编同步机制与操作系统自身同步机制的效率进行比较和分析。


3 实验要求

同步机制及应用编程实现与比较实验功能设计要求:
(1) 银行账户转账同步问题的抽象及未采取同步控制情况下的编程实现;
(2) 基于 Peterson 算法的银行账户转账同步问题解决方案;
(3) 基于 Windows(或 Linux)操作系统同步机制的银行账户转账同步问题解决方案;
(4) Peterson 算法同步机制和 Windows(或 Linux)操作系统同步机制的效率比较。这里,银行账户转账假定为两个账户 nAccount1、 nAccount2(均定义为初值是 0 的整型全局变量) 之间进行,一个转出、一个转入,转账金额随机产生。整个程序共设立该两个账户之间的转账线程两个,且线程功能设计逻辑完全一致,不妨设转账线程的主体逻辑结构为如下的循环:

int nLoop = 0; int nTemp1, nTemp2, nRandom; do { nRandom = rand(); nTemp1 = nAccount1; nTemp2 = nAccount2; nAccount1 = nTemp1 + nRandom; nAccount2 = nTemp2 - nRandom;2 nLoop++; } while ((nAccount1 + nAccount2) = = 0);

注意,在基于 Peterson 算法或 Windows(或 Linux)操作系统同步机制的银行账户转账同步问题解决方案中,上面的循环结束条件应修正为:while(nLoop < 1000000); 或 while(nLoop < 5000000);同时在该循环体中 nAccount2 = nTemp2 - nRandom;语句后插入如下条件语句:if (nAccount1 + nAccount2 != 0) break;


4 实验步骤

4.1 实验环境

操作系统 5.4.18-1-MANJARO
编译器 gcc 版本 9.2.0 (GCC)

4.2 未采取同步控制情况

主函数如下,使用pthread_cread函数创建线程,pthread_join函数回收线程资源。
OS: 同步机制及应用编程实现与比较 - 图1
线程函数如下,Nloop记录循环次数,nAccount储存两个银行账户的金额,pthread_self()获取当前线程的tid,而使用pthread_exit函数退出线程。
OS: 同步机制及应用编程实现与比较 - 图2
运行结果如下:
OS: 同步机制及应用编程实现与比较 - 图3

4.3 基于 Peterson 算法的同步

通过设置flag和turn来实现多线程银行转账同步。当另一线程的flag为真且turn指向另一线程则当前线程阻塞。
OS: 同步机制及应用编程实现与比较 - 图4
两个子线程函数如下,通过while空循环实现阻塞。同时为了与Linux互斥锁机制效率进行比较,调用gettimeofday()函数储存线程运行时间。
OS: 同步机制及应用编程实现与比较 - 图5
OS: 同步机制及应用编程实现与比较 - 图6
运行结果如下:
OS: 同步机制及应用编程实现与比较 - 图7

4.4 基于 Linux互斥锁的同步

互斥锁本质就是一个特殊的全局变量,拥有lock和unlock两种状态,unlock的互斥锁可以由某个线程获得,一旦获得,这个互斥锁会锁上变成lock状态,此后只有该线程由权力打开该锁,其他线程想要获得互斥锁,必须得到互斥锁再次被打开之后。Linux中互斥锁全局变量如下:
OS: 同步机制及应用编程实现与比较 - 图8
子线程函数func如下,通过pthread_mutex_lock()函数锁定互斥锁。使用临界资源后使用pthread_unlock()函数释放互斥锁。
OS: 同步机制及应用编程实现与比较 - 图9
运行结果如下:
OS: 同步机制及应用编程实现与比较 - 图10


5 Peterson算法与Linux互斥锁比较

Peterson算法和Linux互斥锁都是一种很好很简便的控制线程同步机制。对于临界资源有很严格的控制。从运行的结果来看,Peterson算法和Linux互斥锁都能实现并发线程同步。但是通过两者的运行时间来看,两种机制效率是不同的,相较而言Pterson算法的效率更高一筹,而Linux互斥锁机制略逊于Pterson算法。


6 实验总结

本实验研究了多线程同步机制及模拟银行转账进行应用编程。通过银行转账分别模拟了未采取同步机制、基于peterson算法的同步机制和基于Linux互斥锁的同步机制。并将基于peterson算法的同步机制与基于Linux互斥锁的同步机制二者效率进行了比较。通过模拟未采取同步机制的银行转账过程,我们深刻的理解了线程同步的重要性。剩下两个实验让我们对peterson算法有了更深的理解,并且对Linux互斥锁机制有所了解。这对理解操作系统线程控制措施有所促进。就实验难度而言,主要集中于对Linux平台的c编程技能上,当然很多API网上有很多教程和实例,这降低了实验难度。


7 实验源码

  1. // 未采取控制措施
  2. #include<pthread.h>
  3. #include<stdio.h>
  4. #include<stdlib.h>
  5. #include<string.h>
  6. #include<unistd.h>
  7. #include<time.h>
  8. #define RAND(x,y) (rand()%((y)-(x)+1)+x)
  9. int nAccount1 = 0,nAccount2=0;
  10. void *func()
  11. {
  12. int Nloop = 0;
  13. int Ntemp1, Ntemp2;
  14. int r;
  15. Ntemp1 = nAccount1;
  16. Ntemp2 = nAccount2;
  17. do {
  18. r = RAND(0, 1000);
  19. nAccount1 = Ntemp1 + r;
  20. nAccount2 = Ntemp2 - r;
  21. Ntemp1 = nAccount1;
  22. Ntemp2 = nAccount2;
  23. Nloop++;
  24. } while ((nAccount1 + nAccount2) == 0);
  25. printf("Thread: %ld Loop: %d\n", pthread_self(), Nloop);
  26. pthread_exit((void *)1);
  27. }
  28. int main()
  29. {
  30. pthread_t tids[2];
  31. srand(time(0));
  32. pthread_create(&tids[0],NULL,&func,NULL);
  33. pthread_create(&tids[1],NULL,&func,NULL);
  34. printf("未采取同步控制:\n");
  35. void *retv;
  36. pthread_join(tids[0],&retv);
  37. pthread_join(tids[1],&retv);
  38. return 0;
  39. }
  1. // peterson算法
  2. #include<pthread.h>
  3. #include<stdio.h>
  4. #include<stdlib.h>
  5. #include<string.h>
  6. #include<unistd.h>
  7. #include<time.h>
  8. #include<sys/time.h>
  9. #define RAND(x,y) (rand()%((y)-(x)+1)+x)
  10. int nAccount1 = 0,nAccount2=0;
  11. int flag[2] = { 0,0};
  12. int turn = 0;
  13. void *func1() {
  14. int Nloop = 0;
  15. int nTemp1, nTemp2;
  16. int r;
  17. nTemp1 = nAccount1;
  18. nTemp2 = nAccount2;
  19. struct timeval tv1,tv2;
  20. gettimeofday(&tv1,NULL);
  21. do {
  22. flag[0] = 1;
  23. turn = 1;
  24. while (flag[1]&& turn == 1);
  25. r = RAND(0, 1000);
  26. nAccount1 = nTemp1 + r;
  27. nAccount2 = nTemp2 - r;
  28. nTemp1 = nAccount1;
  29. nTemp2 = nAccount2;
  30. Nloop++;
  31. if ((nAccount1 + nAccount2) != 0 )
  32. break;
  33. flag[0] = 0;
  34. } while (Nloop < 1000000);
  35. gettimeofday(&tv2,NULL);
  36. printf("Thread: %ld Loop: %d %dms\n", pthread_self(), Nloop,(tv2.tv_usec-tv1.tv_usec)/1000);
  37. pthread_exit((void *)1);
  38. }
  39. void *func2() {
  40. int Nloop = 0;
  41. int nTemp1, nTemp2;
  42. int r;
  43. nTemp1 = nAccount1;
  44. nTemp2 = nAccount2;
  45. struct timeval tv1,tv2;
  46. gettimeofday(&tv1,NULL);
  47. do {
  48. flag[1] = 1;
  49. turn = 0;
  50. while (flag[0]&& turn == 0);
  51. r = RAND(0, 1000);
  52. nAccount1 = nTemp1 + r;
  53. nAccount2 = nTemp2 - r;
  54. nTemp1 = nAccount1;
  55. nTemp2 = nAccount2;
  56. Nloop++;
  57. if ((nAccount1 + nAccount2) != 0)
  58. break;
  59. flag[1] = 0;
  60. } while (Nloop < 1000000);
  61. gettimeofday(&tv2,NULL);
  62. printf("Thread: %ld Loop: %d %dms\n", pthread_self(), Nloop,(tv2.tv_usec-tv1.tv_usec)/1000);
  63. pthread_exit((void *)1);
  64. }
  65. int main()
  66. {
  67. pthread_t tids[2];
  68. srand(time(0));
  69. pthread_create(&tids[0],NULL,&func1,NULL);
  70. pthread_create(&tids[1],NULL,&func2,NULL);
  71. printf("Peterson算法同步控制:\n");
  72. void *retv;
  73. pthread_join(tids[0],&retv);
  74. pthread_join(tids[1],&retv);
  75. return 0;
  76. }
  1. // Linux互斥锁机制
  2. #include<pthread.h>
  3. #include<stdio.h>
  4. #include<stdlib.h>
  5. #include<string.h>
  6. #include<unistd.h>
  7. #include<time.h>
  8. #include<sys/time.h>
  9. #define RAND(x,y) (rand()%((y)-(x)+1)+x)
  10. int nAccount1 = 0,nAccount2=0;
  11. pthread_mutex_t mutex_x=PTHREAD_MUTEX_INITIALIZER;
  12. void *func() {
  13. int Nloop = 0;
  14. int nTemp1, nTemp2;
  15. int r;
  16. nTemp1 = nAccount1;
  17. nTemp2 = nAccount2;
  18. struct timeval tv1,tv2;
  19. gettimeofday(&tv1,NULL);
  20. do {
  21. pthread_mutex_lock(&mutex_x);
  22. r = RAND(0, 1000);
  23. nAccount1 = nTemp1 + r;
  24. nAccount2 = nTemp2 - r;
  25. nTemp1 = nAccount1;
  26. nTemp2 = nAccount2;
  27. Nloop++;
  28. if ((nAccount1 + nAccount2) != 0)
  29. break;
  30. pthread_mutex_unlock(&mutex_x);
  31. } while (Nloop < 1000000);
  32. gettimeofday(&tv2,NULL);
  33. printf("Thread: %ld Loop: %d %ldms\n", pthread_self(), Nloop,(tv2.tv_sec-tv1.tv_sec)*1000+(tv2.tv_usec-tv1.tv_usec)/1000);
  34. pthread_exit((void *)1);
  35. }
  36. int main()
  37. {
  38. pthread_t tids[2];
  39. srand(time(0));
  40. pthread_create(&tids[0],NULL,&func,NULL);
  41. pthread_create(&tids[1],NULL,&func,NULL);
  42. printf("Linux互斥锁同步控制:\n");
  43. void *retv;
  44. pthread_join(tids[0],&retv);
  45. pthread_join(tids[1],&retv);
  46. return 0;
  47. }