首页 > 编程语言 >操作系统实验:同步机制及应用编程实现与比较——银行转账问题

操作系统实验:同步机制及应用编程实现与比较——银行转账问题

时间:2022-10-16 21:44:05浏览次数:52  
标签:转账 操作系统 int 编程 nAccount1 nAccount2 nRandom printf

1.实验内容及要求

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

同步机制及应用编程实现与比较实验功能设计要求:

(1)银行账户转账同步问题的抽象及未采取同步控制情况下的编程实现;

(2)基于 Peterson 算法的银行账户转账同步问题解决方案;

(3)基于 Windows(或 Linux)操作系统同步机制的银行账户转账同步问题解决方案;

(4)Peterson 算法同步机制和 Windows(或 Linux)操作系统同步机制的效率比较。

 

2.开发/运行/测试环境:

操作系统:Windows10

平台工具集:Visual Studio 2022 (v143)

C++语言标准:ISO C++17 标准 (/std:c++17)

C语言标准:默认(旧 MSVC)

 

3.源代码

  (1)无同步互斥线程

 1 //无同步互斥线程
 2 #include<stdio.h>
 3 #include<time.h>
 4 #include<windows.h>
 5 #include<iostream>
 6 using namespace std;
 7 
 8 //全局变量
 9 int nAccount1 = 0, nAccount2 = 0;//转入账户的原金额,转出账户的原金额
10 int nLoop1 = 0;//2向1转账次数
11 int nLoop2 = 0;//1向2转账次数
12 
13 DWORD WINAPI Tran_2to1(HANDLE Thread)//2向1转账
14 {
15     int nTemp1, nTemp2, nRandom;
16 
17     do
18     {
19         nRandom = rand()%1000;
20         printf("转账金额:%d    2 to 1\n", nRandom);
21         nTemp1 = nAccount1;
22         nTemp2 = nAccount2;
23         nAccount1 = nTemp1 + nRandom;
24         printf("1账户余额:%d\n", nAccount1);
25         nAccount2 = nTemp2 - nRandom;
26         printf("2账户余额:%d\n", nAccount2);
27         nLoop1++;
28         printf("2向1转账次数:第%d次\n\n", nLoop1);
29 
30     } while ((nAccount1 + nAccount2) == 0);
31     return 0;
32 }
33 
34 DWORD WINAPI Tran_1to2(HANDLE Thread)//1向2转账
35 {
36     int nTemp1, nTemp2, nRandom;
37     do
38     {
39         nRandom = rand()%1000;
40         printf("转账金额:%d    1 to 2\n", nRandom);
41         nTemp1 = nAccount1;
42         nTemp2 = nAccount2;
43         nAccount1 = nTemp1 - nRandom;
44         printf("1账户余额:%d\n", nAccount1);
45         nAccount2 = nTemp2 + nRandom;
46         printf("2账户余额:%d\n", nAccount2);
47         nLoop2++;
48         printf("1向2转账次数:第%d次\n\n", nLoop2);
49 
50     } while ((nAccount1 + nAccount2) == 0);
51     return 0;
52 }
53 
54 int main()
55 {
56     
57     HANDLE Thread[2];
58     DWORD starttime, endtime;
59     srand(unsigned(time(NULL)));
60 
61     starttime = GetTickCount();
62 
63     Thread[0] = CreateThread(NULL, 0, Tran_2to1, NULL, 0, NULL);//创建线程函数
64     Thread[1] = CreateThread(NULL, 0, Tran_1to2, NULL, 0, NULL);
65     WaitForMultipleObjects(2, Thread, TRUE, INFINITE);//等待
66     printf("2向1转账总次数:%d次\n", nLoop1);
67     printf("2向1转账总次数:%d次\n", nLoop2);
68     
69     endtime = GetTickCount();
70     printf("运行时间为:%ld ms\n", endtime - starttime);
71 
72     CloseHandle(Thread[0]);//结束线程
73     CloseHandle(Thread[1]);
74 
75     return 0;
76 }

  (2) Peterson算法

Peterson算法是基于双线程互斥访问的LockOne与LockTwo算法而来。LockOne算法使用一个flag布尔数组,LockTwo使用一个turn的整型量,都实现了互斥,但是都存在死锁的可能。Peterson算法把这两种算法结合起来,完美地用软件实现了双线程互斥问题。

算法使用两个控制变量flag与turn. 其中flag[n]的值为真,表示ID号为n的进程希望进入该临界区.,变量turn保存有权访问共享资源的进程的ID号。

 

 

当某一进程的flag被置为true后:

若另一进程直接执行到turn语句,会不得不在while中等待;

若另一进程执行到flag语句后又轮到原进程,则原进程在while中等待,先执行另一进程。

 

//Peterson算法
#include<stdio.h>
#include<time.h>
#include<windows.h>
#include<iostream>
using namespace std;

//全局变量
int nAccount1 = 0, nAccount2 = 0;//转入账户的原金额,转出账户的原金额
int nLoop1 = 0;//2向1转账次数
int nLoop2 = 0;//1向2转账次数

bool flag1 = false;
bool flag2 = false;
int turn = 1;



DWORD WINAPI Tran_2to1(HANDLE Thread)//2向1转账
{
    int nTemp1, nTemp2, nRandom;
    
    do
    {
        nRandom = rand() % 1000;
        printf("转账金额:%d    2 to 1\n", nRandom);

        flag1 = true; turn = 2;
        while (flag2 && turn == 2);
        /*******************临界区**********************/
        nTemp1 = nAccount1;
        nTemp2 = nAccount2;
        nAccount1 = nTemp1 + nRandom;
        printf("1账户余额:%d\n", nAccount1);
        nAccount2 = nTemp2 - nRandom;
        printf("2账户余额:%d\n", nAccount2);
        /*******************临界区**********************/
        flag1 = false;

        nLoop1++;
        printf("2向1转账次数:第%d次\n\n", nLoop1);

    } while (nLoop1 < 1000000);
    return 0;
}

DWORD WINAPI Tran_1to2(HANDLE Thread)//1向2转账
{
    int nTemp1, nTemp2, nRandom;
    

    do
    {
        nRandom = rand() % 1000;
        printf("转账金额:%d    1 to 2\n", nRandom);

        flag2 = true; turn = 1;
        while (flag1 && turn == 1);
        /*******************临界区**********************/
        nTemp1 = nAccount1;
        nTemp2 = nAccount2;
        nAccount1 = nTemp1 - nRandom;
        printf("1账户余额:%d\n", nAccount1);
        nAccount2 = nTemp2 + nRandom;
        printf("2账户余额:%d\n", nAccount2);
        /*******************临界区**********************/
        flag2 = false;

        nLoop2++;
        printf("1向2转账次数:第%d次\n\n", nLoop2);

    } while (nLoop2 < 1000000);
    return 0;
}

int main()
{

    HANDLE Thread[2];
    DWORD starttime, endtime;
    srand(unsigned(time(NULL)));

    starttime = GetTickCount();

    Thread[0] = CreateThread(NULL, 0, Tran_2to1, NULL, 0, NULL);//创建线程函数
    Thread[1] = CreateThread(NULL, 0, Tran_1to2, NULL, 0, NULL);
    WaitForMultipleObjects(2, Thread, TRUE, INFINITE);//等待
    printf("2向1转账总次数:%d次\n", nLoop1);
    printf("2向1转账总次数:%d次\n", nLoop2);

    endtime = GetTickCount();
    printf("运行时间为:%ld ms\n", endtime - starttime);
    
    CloseHandle(Thread[0]);//结束线程
    CloseHandle(Thread[1]);

    return 0;
}

  (3)windows同步互斥线程

 1 //windows同步互斥线程
 2 #include<stdio.h>
 3 #include<time.h>
 4 #include<windows.h>
 5 #include<iostream>
 6 using namespace std;
 7 
 8 //全局变量
 9 int nAccount1 = 0, nAccount2 = 0;//转入账户的原金额,转出账户的原金额
10 int nLoop1 = 0;//2向1转账次数
11 int nLoop2 = 0;//1向2转账次数
12 HANDLE mutex;//声明一个内核对象
13 
14 DWORD WINAPI Tran_2to1(HANDLE Thread)//2向1转账
15 {
16     int nTemp1, nTemp2, nRandom;
17 
18     do
19     {
20         WaitForSingleObject(mutex, INFINITE); //等待互斥量触发
21 
22         nRandom = rand() % 1000;
23         printf("转账金额:%d    2 to 1\n", nRandom);
24         nTemp1 = nAccount1;
25         nTemp2 = nAccount2;
26         nAccount1 = nTemp1 + nRandom;
27         printf("1账户余额:%d\n", nAccount1);
28         nAccount2 = nTemp2 - nRandom;
29         printf("2账户余额:%d\n", nAccount2);
30         if (nAccount1 + nAccount2 != 0)
31             break;
32         nLoop1++;
33         printf("2向1转账次数:第%d次\n\n", nLoop1);
34 
35         ReleaseMutex(mutex);//释放互斥量
36 
37     } while (nLoop1 < 100000);;
38     return 0;
39 }
40 
41 DWORD WINAPI Tran_1to2(HANDLE Thread)//1向2转账
42 {
43     int nTemp1, nTemp2, nRandom;
44     do
45     {
46         WaitForSingleObject(mutex, INFINITE); //等待互斥量触发
47 
48         nRandom = rand() % 1000;
49         printf("转账金额:%d    1 to 2\n", nRandom);
50         nTemp1 = nAccount1;
51         nTemp2 = nAccount2;
52         nAccount1 = nTemp1 - nRandom;
53         printf("1账户余额:%d\n", nAccount1);
54         nAccount2 = nTemp2 + nRandom;
55         printf("2账户余额:%d\n", nAccount2);
56         if (nAccount1 + nAccount2 != 0)
57             break;
58         nLoop2++;
59         printf("1向2转账次数:第%d次\n\n", nLoop2);
60 
61         ReleaseMutex(mutex);//释放互斥量
62 
63     } while (nLoop2 < 100000);
64     return 0;
65 }
66 
67 int main()
68 {
69 
70     HANDLE Thread[2];
71     DWORD starttime, endtime;
72     srand(unsigned(time(NULL)));
73 
74     mutex = CreateMutex(NULL, FALSE, NULL);// 创建互斥量,初始化为触发状态
75 
76     starttime = GetTickCount();
77 
78     Thread[0] = CreateThread(NULL, 0, Tran_2to1, NULL, 0, NULL);//创建线程函数
79     Thread[1] = CreateThread(NULL, 0, Tran_1to2, NULL, 0, NULL);
80     WaitForMultipleObjects(2, Thread, TRUE, INFINITE);//一直等待,直到所有子线程全部返回
81     printf("2向1转账总次数:%d次\n", nLoop1);
82     printf("2向1转账总次数:%d次\n", nLoop2);
83 
84     endtime = GetTickCount();
85     printf("运行时间为:%ld ms\n", endtime - starttime);
86 
87     CloseHandle(Thread[0]);//结束线程
88     CloseHandle(Thread[1]);
89 
90     return 0;
91 }

4.疑难解惑及经验教训

如果采用#include<thread>及其方法,会导致无法nAccount1 + nAccount2 == 0恒成立,从而使程序无法跳出循环。尚不清楚为何会出现这种情况。

最初我认为是线程构建失败,从而导致两个线程函数退化为普通函数,导致仅有一个函数被执行且在此函数中不断循环,从而解释为何nAccount1 + nAccount2 == 0恒成立。但经过测试,发现两个函数都得到了执行,从而排除了此种情况。

 1 /*//无同步互斥线程
 2 #include<stdio.h>
 3 #include<time.h>
 4 #include<thread>
 5 #include<iostream>
 6 using namespace std;
 7 
 8 void Tran_2to1(int nAccount1, int nAccount2);//2向1转账
 9 void Tran_1to2(int nAccount1, int nAccount2);//1向2转账
10 
11 //全局变量
12 int nAccount1 = 0, nAccount2 = 0;//转入账户的原金额,转出账户的原金额
13 
14 int main()
15 {
16 
17     srand(unsigned(time(NULL)));
18 
19     thread AC1(Tran_2to1, nAccount1, nAccount2);
20     thread AC2(Tran_1to2, nAccount1, nAccount2);
21 
22     AC1.join();
23     AC2.join();
24     return 0;
25 }
26 
27 void Tran_2to1(int nAccount1, int nAccount2)//2向1转账
28 {
29     int nTemp1, nTemp2, nRandom;
30     int nLoop = 0;//2向1转账次数
31 
32     do
33     {
34         nRandom = rand()%1000;
35         printf("转账金额:%d    2 to 1\n", nRandom);
36         nTemp1 = nAccount1;
37         nTemp2 = nAccount2;
38         nAccount1 = nTemp1 + nRandom;
39         printf("1账户余额:%d\n", nAccount1);
40         nAccount2 = nTemp2 - nRandom;
41         printf("2账户余额:%d\n\n", nAccount2);
42         nLoop++;
43         printf("2向1转账次数:第%d次\n", nLoop);
44         
45     } while ((nAccount1 + nAccount2) == 0);
46     return;
47 }
48 
49 void Tran_1to2(int nAccount1, int nAccount2)//1向2转账
50 {
51     int nTemp1, nTemp2, nRandom;
52     int nLoop = 0;//1向2转账次数
53 
54     do
55     {
56         nRandom = rand()%1000;
57         printf("转账金额:%d    1 to 2\n", nRandom);
58         nTemp1 = nAccount1;
59         nTemp2 = nAccount2;
60         nAccount1 = nTemp1 - nRandom;
61         printf("1账户余额:%d\n", nAccount1);
62         nAccount2 = nTemp2 + nRandom;
63         printf("2账户余额:%d\n\n", nAccount2);
64         nLoop++;
65         printf("1向2转账次数:第%d次\n", nLoop);
66 
67     } while ((nAccount1 + nAccount2) == 0);
68     return;
69 }*/

5.结论与体会

分别使用Peterson算法和Windows的同步互斥机制对两个进程同样执行100000次,可以看到Windows的同步互斥机制所用时间更短,性能更佳。

体会:实验要提前做,不要等到deadline附近。虽然日期的迫近会使效率变高,但也会带来不必要的焦虑。此外,时间的不充裕也会对实验质量带来影响。

建议:化整为零,步步扎营,稳扎稳打。

操作系统的知识博大精深,关联了多门专业课的知识,学好操作系统极其有必要。

标签:转账,操作系统,int,编程,nAccount1,nAccount2,nRandom,printf
From: https://www.cnblogs.com/jeffrey188/p/16797298.html

相关文章