操作系统用同步线程解决银行转账的典型问题,在做实验的时候遇到了很多问题,在这里记录一下解决办法,如果有遇到一样问题的人,希望这篇文章可以帮忙。(博主是在windows下做的)
同步机制及应用编程实现与比较实验功能设计要求:
(1) 银行账户转账同步问题的抽象及未采取同步控制情况下的编程
实现;
(2) 基于Peterson算法的银行账户转账问题解决方案;
(3) 基于Windows(Linux)操作系统同步机制的银行账户转账同步
问题解决方案;
(4)Peterson算法同步机制和Windows(或Linux)操作系统同步机制的效率比较。
这里,银行账户转账假定为两个账户nAccount1、nAccount2(均定义为初值是0的整性全局变量)之间进行,一个转出、一个转入,转账金额随机产生。整个程序共设立该两个账户之间的转账线程两个,且线程功能设计逻辑完全一致。
没有互斥的线程:
问题一:
设置的count是1000,但是最后运行结束的时候循环只进行了不到10次,这合理吗?
解决:
答案当然是合理的。因为在没有互斥信号量的线程运行过程中,线程的执行的随机的,无法保证两个账户的和在每笔交易之后仍然是定值,因此程序在什么时候结束都是有可能的,循环执行多少次都是随机的。
同步机制
问题二:
当循环次数设置到1000000次时,最后的输出结果中只看到了一个线程输出的循环次数,没有看到另一个,这是为什么呢?
解决:
在windows操作系统的同步机制下线程的执行顺序时随机的,最后只能看到其中一个线程的结果,有可能是另一个线程在很早之前已经循环完1000000次结束了,已经输出过了结果。
peterson算法
问题三:
最后的输出结果永远都是只差一个循环是为什么?
解决:
这就是peterson算法的谦让。根据peterson算法的原理,只有在flag的值从1变到0的时候另一个线程才会开始执行,因为在循环的最后会执行flag值的变换操作,所以两个线程是交替执行的,也因此,最后输出结果之间一定会差一个循环。
一些小问题
1、如果设置了两个线程函数,需要使用两个nLoop来记录线程执行的次数。如果只有一个nLoop的话,在同步机制中,同时修改其值会发生错误。
2、使用互斥信号量mutex一定要初始化
HANDLE mutex = CreateMutex(NULL, FALSE, NULL);
3、peterson算法的主体部分放到线程函数的循环体中。
源代码
//Peterson算法
#include<stdio.h>
#include<windows.h>
#include<stdlib.h>
#include<time.h>
#define COUNT 1000000
int flag[2] = { 0, 0 };
int nLoop0 = 0, nLoop1 = 0;//转账次数
int nTemp1, nTemp2, nRandom;//账户临时存储1、2 转账随机数
int nAccount1 = 0, nAccount2 = 0;//账户1、2
int turn;
DWORD WINAPI Fun0(HANDLE Thread)
{
do
{
flag[0] = 1;
turn = 1;
while ((flag[turn] == 1) && (turn == 1));
nRandom = rand() % 1000;
nTemp1 = nAccount1;
printf("nAccount1当前资金为:%d\n", nTemp1);
nTemp2 = nAccount2;
printf("nAccount2当前资金为:%d\n", nTemp2);
nAccount1 = nTemp1 + nRandom;//账户1转入
printf("nAccount1收到资金 %d,现资金为:%d\n", nRandom, nAccount1);
nAccount2 = nTemp2 - nRandom;//账户2转出
printf("nAccount2转出资金 %d,现资金为:%d\n", nRandom, nAccount2);
nLoop0++;//转账次数+1
flag[0] = 0;//解除另一个进程的使用限制
} while (((nAccount1 + nAccount2) == 0) && (nLoop0 < COUNT));
printf("Fun0循环次数为 %d 次\n", nLoop0);
return 0;
}
DWORD WINAPI Fun1(HANDLE Thread)
{
do
{
flag[1] = 1;
turn = 0;
while ((flag[turn] == 1) && (turn == 0));
nRandom = rand() % 1000;
nTemp1 = nAccount1;
printf("nAccount1当前资金为:%d\n", nTemp1);
nTemp2 = nAccount2;
printf("nAccount2当前资金为:%d\n", nTemp2);
nAccount1 = nTemp1 + nRandom;//账户1转入
printf("nAccount1收到资金 %d,现资金为:%d\n", nRandom, nAccount1);
nAccount2 = nTemp2 - nRandom;//账户2转出
printf("nAccount2转出资金 %d,现资金为:%d\n", nRandom, nAccount2);
nLoop1++;//转账次数+1
flag[1] = 0;//解除另一个进程的使用限制
}while (((nAccount1 + nAccount2) == 0) && (nLoop1 < COUNT));
printf("Fun1循环次数为 %d 次\n", nLoop1);
return 0;
}
int main()
{
HANDLE Thread[2];
DWORD starttime, endtime;
starttime = GetTickCount();//开始时间
Thread[0] = CreateThread(NULL, 0, Fun0, NULL, 0, NULL);//创建线程
Thread[1] = CreateThread(NULL, 0, Fun1, NULL, 0, NULL);
WaitForMultipleObjects(2, Thread, TRUE, INFINITE);//等待
endtime = GetTickCount();//结束时间
printf("运行时间为:%ld ms\n", endtime - starttime);
CloseHandle(Thread[0]);//结束线程
CloseHandle(Thread[1]);
return 0;
}
//Windows
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#define COUNT 1000000
int nLoop0 = 0,nLoop1 = 0;//转账次数
int nTemp1, nTemp2, nRandom;//账户临时存储1、2 转账随机数
int nAccount1 = 0, nAccount2 = 0;//账户1、2
HANDLE mutex;
DWORD WINAPI Fun0(HANDLE Thread)
{
do
{
WaitForSingleObject(mutex, INFINITE);
nRandom = rand() % 1000;
nTemp1 = nAccount1;
printf("nAccount1当前资金为:%d\n", nTemp1);
nTemp2 = nAccount2;
printf("nAccount2当前资金为:%d\n", nTemp2);
nAccount1 = nTemp1 + nRandom;//账户1转入
printf("nAccount1收到资金 %d,现资金为:%d\n", nRandom, nAccount1);
nAccount2 = nTemp2 - nRandom;//账户2转出
printf("nAccount2转出资金 %d,现资金为:%d\n", nRandom, nAccount2);
nLoop0++;//转账次数+1
ReleaseMutex(mutex);
} while (((nAccount1 + nAccount2) == 0) && (nLoop0 < COUNT));
printf("Fun0循环次数为 %d 次\n", nLoop0);
return 0;
}
DWORD WINAPI Fun1(HANDLE Thread)
{
do
{
WaitForSingleObject(mutex, INFINITE);
nRandom = rand() % 1000;
nTemp1 = nAccount1;
printf("nAccount1当前资金为:%d\n", nTemp1);
nTemp2 = nAccount2;
printf("nAccount2当前资金为:%d\n", nTemp2);
nAccount1 = nTemp1 + nRandom;//账户1转入
printf("&&&nAccount1收到资金 %d,现资金为:%d\n", nRandom, nAccount1);
nAccount2 = nTemp2 - nRandom;//账户2转出
printf("&&&nAccount2转出资金 %d,现资金为:%d\n", nRandom, nAccount2);
nLoop1++;//转账次数+1
ReleaseMutex(mutex);
} while (((nAccount1 + nAccount2) == 0) && (nLoop1 < COUNT));
printf("Fun1循环次数为 %d 次\n", nLoop1);
return 0;
}
int main()
{
HANDLE Thread[2];
DWORD starttime, endtime;
starttime = GetTickCount();
mutex = CreateMutex(NULL, FALSE, NULL);
Thread[0] = CreateThread(NULL, 0, Fun0, NULL, 0, NULL);//创建线程函数
Thread[1] = CreateThread(NULL, 0, Fun1, NULL, 0, NULL);
WaitForMultipleObjects(2, Thread, TRUE, INFINITE);//等待
endtime = GetTickCount();
printf("运行时间为:%ld ms\n", endtime - starttime);
CloseHandle(Thread[0]);//结束线程
CloseHandle(Thread[1]);
return 0;
}
//Windows无互斥
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#define COUNT 100
int nLoop0 = 0,nLoop1 = 0;//转账次数
int nTemp1, nTemp2, nRandom;//账户临时存储1、2 转账随机数
int nAccount1 = 0, nAccount2 = 0;//账户1、2
DWORD WINAPI Fun0(HANDLE Thread)
{
do
{
nRandom = rand() % 1000;
nTemp1 = nAccount1;
printf("nAccount1当前资金为:%d\n", nTemp1);
nTemp2 = nAccount2;
printf("nAccount2当前资金为:%d\n", nTemp2);
nAccount1 = nTemp1 + nRandom;//账户1转入
printf("nAccount1收到资金 %d,现资金为:%d\n", nRandom, nAccount1);
nAccount2 = nTemp2 - nRandom;//账户2转出
printf("nAccount2转出资金 %d,现资金为:%d\n", nRandom, nAccount2);
nLoop0++;//转账次数+1
} while (((nAccount1 + nAccount2) == 0) && (nLoop0 < COUNT));
printf("Fun0循环次数为 %d 次\n", nLoop0);
return 0;
}
DWORD WINAPI Fun1(HANDLE Thread)
{
do
{
nRandom = rand() % 1000;
nTemp1 = nAccount1;
printf("nAccount1当前资金为:%d\n", nTemp1);
nTemp2 = nAccount2;
printf("nAccount2当前资金为:%d\n", nTemp2);
nAccount1 = nTemp1 + nRandom;//账户1转入
printf("nAccount1收到资金 %d,现资金为:%d\n", nRandom, nAccount1);
nAccount2 = nTemp2 - nRandom;//账户2转出
printf("nAccount2转出资金 %d,现资金为:%d\n", nRandom, nAccount2);
nLoop1++;//转账次数+1
} while (((nAccount1 + nAccount2) == 0) && (nLoop1 < COUNT));
printf("Fun1循环次数为 %d 次\n", nLoop1);
return 0;
}
int main()
{
HANDLE Thread[2];
DWORD starttime, endtime;
starttime = GetTickCount();
Thread[0] = CreateThread(NULL, 0, Fun0, NULL, 0, NULL);//创建线程函数
Thread[1] = CreateThread(NULL, 0, Fun1, NULL, 0, NULL);
WaitForMultipleObjects(2, Thread, TRUE, INFINITE);//等待
endtime = GetTickCount();
printf("运行时间为:%ld ms\n", endtime - starttime);
CloseHandle(Thread[0]);//结束线程
CloseHandle(Thread[1]);
return 0;
}