UVa 210 Concurrency Simulator [并发模拟,双端队列]
题意
模拟并发,n个程序切换进行,共享变量。每个时刻只能有一个程序在运行,每次运行分配了大小为quantum的时间,在一时间内运行程序中耗时为t[i]的指令。
运行quantum时间后,程序进入[准备队列]队尾。指令lock在实际的并发中作用是申请对变量独占访问(本题中只改变程序顺序),如果已经有一个程序执行了lock,另一个程序再次执行lock将会被添加到[阻塞队列]队尾,quantum直接结束,不进入[准备队列]。直到原lock程序执行unlock,[阻塞队列]队首程序出队,进入[等待队列]队首。
分析
[等待队列]可以插入队首,因此是个[双端队列]。每一个程序执行到哪条指令可以用一个指针/索引表示。
代码
1 #include<iostream> 2 #include<cstdio> 3 #include<vector> 4 #include<string> 5 #include<queue> 6 #include<deque> 7 #include<cstring> 8 9 using namespace std; 10 11 typedef struct { 12 vector<string> statements; //存放指令语句 13 int eip = 0; //当前指令指针 14 bool pushed = 0; //标志程序是否入阻塞队 15 } Program; 16 17 vector<Program> programs; 18 19 int test_cases, n, t[5], q, vars[26]; 20 21 queue<int> blocked_queue; //push pop 22 deque<int> ready_queue; //push_front push_back pop_front pop_back 23 24 bool glock = 0; 25 int pid, time_count; 26 27 void assign_ins() 28 { 29 int &eip = programs[pid].eip; 30 string &ins = programs[pid].statements[eip]; 31 int int_const = stoi(ins.substr(4)); //v = ? 32 vars[ins[0] - 'a'] = int_const; 33 34 eip++; 35 time_count += t[0]; 36 } 37 38 void print_ins() 39 { 40 int &eip = programs[pid].eip; 41 char var = programs[pid].statements[eip][6]; 42 printf("%d: %d\n", pid+1, vars[var - 'a']); 43 44 eip++; 45 time_count += t[1]; 46 } 47 48 bool lock_ins() 49 { 50 //不考虑重复锁 51 if(glock) //当前已经有锁 52 { 53 if(!programs[pid].pushed) 54 { 55 blocked_queue.push(pid); 56 programs[pid].pushed = 1; 57 } 58 time_count += q; //锁住的情况下,什么都做不了,直接超时 59 return 0; 60 } 61 else { 62 glock = 1; 63 64 programs[pid].eip++; 65 time_count += t[2]; 66 return 1; 67 } 68 } 69 70 void unlock_ins() 71 { 72 glock = 0; 73 if(!blocked_queue.empty()) 74 { 75 int blocked_id = blocked_queue.front(); blocked_queue.pop(); 76 programs[blocked_id].pushed = 0; //从等待队列中释放 77 ready_queue.push_front(blocked_id); 78 } 79 80 programs[pid].eip++; 81 time_count += t[3]; 82 } 83 84 void end_ins() 85 { 86 programs[pid].eip++; //eip == programs[pid].statements.size() 87 time_count += q; 88 } 89 90 91 int main() 92 { 93 scanf("%d", &test_cases); 94 while(test_cases--) 95 { 96 scanf("%d", &n); 97 for(int i = 0; i < 5; i++) 98 scanf("%d", t+i); 99 scanf("%d ", &q); 100 101 memset(vars, 0, sizeof(vars)); 102 programs.clear(); 103 programs.resize(n); 104 105 string str; 106 for(int i = 0; i < n; i++) 107 while(getline(cin, str)) 108 { 109 programs[i].statements.push_back(str); 110 if(str[2] == 'd') 111 break; 112 } 113 114 //程序入队 115 for(int i = 0; i < n; i++) 116 ready_queue.push_back(i); 117 118 while(!ready_queue.empty()) 119 { 120 pid = ready_queue.front(); ready_queue.pop_front(); 121 time_count = 0; 122 123 bool ready_flag = 1; 124 while(time_count < q) //程序时间内 125 { 126 int &eip = programs[pid].eip; 127 switch(programs[pid].statements[eip][2]) //变量名问题,不能用首字符 128 { 129 case '=': 130 assign_ins(); 131 break; 132 case 'i': 133 print_ins(); 134 break; 135 case 'c': 136 ready_flag = lock_ins(); 137 break; 138 case 'l': 139 unlock_ins(); 140 break; 141 case 'd': 142 end_ins(); 143 ready_flag = 0; 144 break; 145 default: 146 break; 147 } 148 } 149 if(ready_flag) 150 ready_queue.push_back(pid); 151 } 152 if(test_cases > 0) 153 printf("\n"); 154 } 155 return 0; 156 }
小结
写代码过程中的bug总结:
1.一定要注意保证循环可以退出,循环不能正常退出,输出会混乱(甚至不符合代码逻辑)。特别注意:*循环条件不是计数,而是队列为空之类的判断,保证队列一定会运行到为空。
2.根据输入条件选择对应处理时,要看清楚判断方式会不会干扰,比如本题中"end"和“e = 1”首字符str[0]都为'e',所以不能选首字符判断。
3.一些标志位,比如Program.pushed,置为1之后要注意有对应的地方将其置为0。
一些扩展的想法:在本题基础上实现并发控制,给IO操作设置耗时t > quantum,不放入准备队列。IO完成产生中断,把它插到准备队列队首。lock后,其他执行修改共享变量的程序进入阻塞队列,等unlock。以上是瞎想的。

浙公网安备 33010602011771号