太阳是黑色的

少年听雨歌楼上,红烛昏罗帐.壮年听雨客舟中,江阔云低断雁叫西风. 而今听雨僧庐下,鬓已星星也! 悲欢离合总无情,一任阶前点滴到天明.

Archive for 4 月, 2005

与老师探讨问题

彭老师:
又来麻烦您了,上次您给我说了关于“约瑟夫问题”的程序以后,我开始注意自己的程序结构的合理性安排。两次上机作业“多项式操作”和“表达式计算”都做好了,这些天终于把“银行的模拟”做好了,请老师看看。我下面大体说一下程序的思路。
首先,我当然用到了“队列”,因为这个程序是“队列”的应用嘛。我使用了2种队列,分别装“刚进入银行的人”和“在某个队上排队的人”,在程序里面是BankIndoor和BankQueue。关于事件的驱动,我思考了很久,最终我采用了一个存放“函数指针”的链表:

1
2
3
4
5
6
typedef struct MethodStruct
{
METHODCALLBACK (*todo)();
METHODSTATE state;
MethodStruct *next;
}MethodNode;

把 每一次要触发的事件都存在这个链表里面,而每一个基本时间段里,都由EnumerateAllMethod()这个函数通过遍历链表来达到出发所有事件的 作用。EnumerateAllMethod通过这个链表每一个节点的state值来判断当前是否激发这个事件。通过这个方法就很容易随机地产生顾客。比 如CustomerArrival()这个函数,每次都产生一个随机数,用于EnumerateAllMethod()判断下一次调用它的时间。关于这种 思路,其实我是参考了Windows的消息触发机制。
在这个事件的链表里面,有三个很重要的事件是CustomerArrival()、 CustomerQueue()、CustomerTODO()。分别处理顾客到来,顾客分队,顾客办理事务这三个事件。这三个函数互相不影响。都由 EnumerateAllMethod统一调用,并且各自监视各自的队列。
最后要说的是关于银行关门的处理,银行到达运营时间的时候产生 READYFORCLOSED的标识,这个时候CustomerArrival()就不触发了,而只触发CustomerQueue()、 CustomerTODO()。当CustomerTODO()检查到所有队列都没有顾客了的时候,就把银行标识改为CLOSED,这样就解决了这个问 题。

通过这次练习,我学到了很多东西,不过也萌发了用C语言来实现事件机制的想法,呵呵,但是我还是觉得有一些难。

程序在Visual C++.NET 2003和Dev-C++下均调试通过。程序运行时间为30秒(可以通过修改#define CLOSETIME 30来进行更改)。

源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
 
//****************************************
#define WAIT_INDOOR 1
#define WAIT_QUEUE 2
#define QUEUES 5
#define FAILED 0
#define SUCCEEDED 1
#define OPEN 1
#define CLOSED 0
#define READYFORCLOSED 2
#define OVERFLOW 2
#define CLOSETIME 30
#define MINTIMEARRIVAL 2
#define MINTIMETODO 10
typedef int EVENTTYPE;
typedef int METHODSTATE;
typedef int METHODCALLBACK;
typedef long SYSTEMTIME;
typedef int STATUS;
typedef int BANKSTATE;
typedef int CustomerID;
//****************************************
typedef struct EventStruct
{
EVENTTYPE detail;
SYSTEMTIME occurtime;
CustomerID ID;
}EventNode;
 
typedef EventNode *Event;
 
typedef struct QueueStruct
{
EventNode item;
QueueStruct *next;
}QueueNode;
 
typedef QueueNode *QueuePtr;
 
typedef struct{
QueuePtr front;
QueuePtr rear;
}LinkQueue;
 
typedef struct CustomerStruct
{
SYSTEMTIME arrivaltime;
SYSTEMTIME duration;
}CustomerNode;
 
typedef struct MethodStruct
{
 
METHODCALLBACK (*todo)();
METHODSTATE state;
MethodStruct *next;
}MethodNode;
 
typedef MethodNode *MethodLink;
//*****************************************
LinkQueue BankIndoor;
LinkQueue BankQueue[QUEUES];
MethodLink Methods;
SYSTEMTIME BankClock;
BANKSTATE BankState;
time_t StartTime,EndTime;
CustomerID IDSeed;
//*****************************************
STATUS InitQueue(LinkQueue &amp;Q)
{
Q.front=Q.rear=new QueueNode;
if (!Q.front) exit(OVERFLOW);
Q.front-&gt;next=NULL;
return SUCCEEDED;
}
 
STATUS DestroyQueue(LinkQueue &amp;Q)
{
while(Q.front)
{
Q.rear=Q.front-&gt;next;
delete Q.front;
Q.front=Q.rear;
}
return SUCCEEDED;
}
 
STATUS EnQueue(LinkQueue &amp;Q,Event e)
{
QueuePtr p;
p=new QueueNode;
if (!p) exit(OVERFLOW);
p-&gt;item=*e;p-&gt;next=NULL;
Q.rear-&gt;next=p;
Q.rear=p;
return SUCCEEDED;
}
 
STATUS DeQueue(LinkQueue &amp;Q,Event &amp;e)
{
QueuePtr p;
if(Q.front==Q.rear) return FAILED;
p=Q.front-&gt;next;
*e=p-&gt;item;
Q.front-&gt;next=p-&gt;next;
if (Q.rear==p)
Q.rear=Q.front;
delete p;
return SUCCEEDED;
}
 
int QueueLength(LinkQueue Q)
{
int numbers=0;
while(Q.front!=Q.rear)
{
Q.front=Q.front-&gt;next;
numbers++;
}
return numbers;
}
 
int PlaceCustomer()
{
int min=0;
for (int i=0;i&lt;QUEUES;i++)
{
if(QueueLength(BankQueue[i])&lt;=QueueLength(BankQueue[min]))
min=i;
}
return min;
}
//*****************************************
METHODCALLBACK CustomerArrival()
{
SYSTEMTIME nexttime;
Event node;
if (BankState==READYFORCLOSED)
return 32767;
node=new EventNode;
node-&gt;detail=WAIT_INDOOR;
node-&gt;occurtime=BankClock;
node-&gt;ID=IDSeed++;
srand((unsigned)time(0));
nexttime=rand()%MINTIMEARRIVAL;
EnQueue(BankIndoor,node);
printf("Customer %d arrived in %d\n",node-&gt;ID,BankClock);
delete node;
return nexttime;
}
 
METHODCALLBACK CustomerQueue()
{
Event node;
int nexttime;
node=new EventNode;
if (DeQueue(BankIndoor,node)!=FAILED)
{
node-&gt;detail=WAIT_QUEUE;
srand((unsigned)time(0));
nexttime=rand()%MINTIMETODO+1;
node-&gt;occurtime=nexttime;
printf("Customer %d placed in queue %d(%d)\n",node-&gt;ID,PlaceCustomer(),nexttime);
EnQueue(BankQueue[PlaceCustomer()],node);
}
delete node;
return 0;
}
 
METHODCALLBACK CustomerTODO()
{
Event node;
int counter=0;
node=new EventNode;
for (int i=0;i&lt;QUEUES;i++)
{
if (DeQueue(BankQueue[i],node)!=FAILED)
{
//printf("%d\n",node-&gt;occurtime);
if (node-&gt;occurtime&lt;=0)
{
printf("Customer %d has left from queue %d\n",node-&gt;ID,i);
}
else
{
node-&gt;occurtime--;
EnQueue(BankQueue[i],node);
}
counter++;
}
}
delete node;
if (counter==0&amp;&amp;BankState==READYFORCLOSED)
BankState=CLOSED;
return 0;
}
//判断是否到了停止营业的时间
METHODCALLBACK CheckForClose()
{
if(BankClock==CLOSETIME)
{
BankState=READYFORCLOSED;
printf("The bank is ready for close.\n");
}
return 0;
}
 
//时钟
METHODCALLBACK Clock()
{
BankClock++;
return 0;
}
 
//****************************************
STATUS InitMethods()
{
if (Methods==NULL)
{
Methods=new MethodNode;
Methods-&gt;state=0;
Methods-&gt;next=NULL;
Methods-&gt;todo=Clock;
return SUCCEEDED;
}
else
return FAILED;
}
 
STATUS RegisterMethod(METHODCALLBACK (*m)())
{
MethodLink pointer;
MethodLink node;
pointer=Methods;
while(pointer-&gt;next!=NULL)
pointer=pointer-&gt;next;
node=new MethodNode;
if (node==NULL)
return FAILED;
node-&gt;state=0;
node-&gt;next=NULL;
node-&gt;todo=m;
pointer-&gt;next=node;
return SUCCEEDED;
}
 
STATUS RegisterAllMethod()
{
RegisterMethod(CheckForClose);
RegisterMethod(CustomerArrival);
RegisterMethod(CustomerQueue);
RegisterMethod(CustomerTODO);
return SUCCEEDED;
}
 
STATUS DeleteAllMethod()
{
MethodLink pointer;
MethodLink node;
pointer=Methods;
while(pointer-&gt;next!=NULL)
{
node=pointer;
pointer=pointer-&gt;next;
delete node;
}
return SUCCEEDED;
}
 
STATUS EnumerateAllMethod()
{
MethodLink pointer;
pointer=Methods;
time(&amp;EndTime);
if (difftime(EndTime,StartTime)&gt;=1.0)
{
time(&amp;StartTime);
while(pointer!=NULL)
{
if(pointer-&gt;state==0)
pointer-&gt;state=pointer-&gt;todo();
else
pointer-&gt;state--;
pointer=pointer-&gt;next;
}
}
return SUCCEEDED;
}
 
STATUS OpenForTheDay()
{
BankClock=0;
BankState=OPEN;
IDSeed=0;
//**********************
InitMethods();
RegisterAllMethod();
InitQueue(BankIndoor);
for (int i=0;i&lt;QUEUES;i++)
InitQueue(BankQueue[i]);
time(&amp;StartTime);
printf("The bank is open.\n");
return SUCCEEDED;
}
 
STATUS CloseForTheDay()
{
DestroyQueue(BankIndoor);
for(int i=0;i&lt;QUEUES;i++)
DestroyQueue(BankQueue[i]);
DeleteAllMethod();
printf("The bank is closed.\n");
return SUCCEEDED;
}
//*****************************************
int main()
{
OpenForTheDay();
while(BankState!=CLOSED)
{
EnumerateAllMethod();
}
CloseForTheDay();
system("PAUSE");
return 0;
}