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 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
| 实验二(上) 进程互斥实验 一、实验环境 Windows 2000,VC++ 6.0。 二、实验目的 1、自己设计程序实现Windows系统中线程的创建; 2、设计实现主线程向子线程传递参数; 3、设计实现线程间的互斥运行。 三、实验原理 1)线程的创建 Win32 提供了一系列的API函数来完成线程的创建、挂起、恢复、终结以及通信等工作。下面将选取其中的一些重要函数进行说明。 ① HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId); 该函数在其调用进程的进程空间里创建一个新的线程,并返回已建线程的句柄,其中各参数说明如下: lpThreadAttributes:指向一个 SECURITY_ATTRIBUTES 结构的指针,该结构决定了线程的安全属性,一般置为 NULL; dwStackSize:指定了线程的堆栈深度,一般都设置为0; lpStartAddress:表示新线程开始执行时代码所在函数的地址,即线程的起始地址。一般情况为(LPTHREAD_START_ROUTINE)ThreadFunc,ThreadFunc 是线程函数名; lpParameter:指定了线程执行时传送给线程的32位参数,即线程函数的参数; dwCreationFlags:控制线程创建的附加标志,可以取两种值。如果该参数为0,线程在被创建后就会立即开始执行;如果该参数为CREATE_SUSPENDED,则系统产生线程后,该线程处于挂起状态,并不马上执行,直至函数ResumeThread被调用; lpThreadId:该参数返回所创建线程的ID; 如果创建成功则返回线程的句柄,否则返回NULL。 ② DWORD SuspendThread(HANDLE hThread); 该函数用于挂起指定的线程,如果函数执行成功,则线程的执行被终止。 ③ DWORD ResumeThread(HANDLE hThread); 该函数用于结束线程的挂起状态,执行线程。 ④ VOID ExitThread(DWORD dwExitCode); 该函数用于线程终结自身的执行,主要在线程的执行函数中被调用。其中参数dwExitCode用来设置线程的退出码。 ⑤ BOOL TerminateThread(HANDLE hThread,DWORD dwExitCode); 一般情况下,线程运行结束之后,线程函数正常返回,但是应用程序可以调用TerminateThread强行终止某一线程的执行。各参数含义如下: hThread:将被终结的线程的句柄; dwExitCode:用于指定线程的退出码。 使用TerminateThread()终止某个线程的执行是不安全的,可能会引起系统不稳定;虽然该函数立即终止线程的执行,但并不释放线程所占用的资源。因此,一般不建议使用该函数。 ⑥ BOOL PostThreadMessage(DWORD idThread, UINT Msg, WPARAM wParam, LPARAM lParam); 该函数将一条消息放入到指定线程的消息队列中,并且不等到消息被该线程处理时便返回。 idThread:将接收消息的线程的ID; Msg:指定用来发送的消息; wParam:同消息有关的字参数; lParam:同消息有关的长参数; 调用该函数时,如果即将接收消息的线程没有创建消息循环,则该函数执行失败。 2)互斥量的创建 互斥量的作用是保证每次只能有一个线程获得互斥量而得以继续执行,使用CreateMutex函数创建: HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, // 安全属性结构指针,可为NULL BOOL bInitialOwner, // 当前建立互斥量是否占有该互斥量 //TRUE表示占有,这样其他线程就不能获得此互斥量也就无法进入由 //该互斥量控制的临界区。FALSE表示不占有该互斥量 LPCTSTR lpName // 信号量的名称,字符数不可多于MAX_PATH //如果遇到同名的其他信号量函数就会失败,如果遇到同类信号同名 //也要注意变化 ); 3)阻塞函数 如果等待的信号量不可用,那么线程就会挂起,直到信号可用线程才会被唤醒,该函数会自动修改信号,如Event,线程被唤醒之后Event信号会变得无信号,Mutex、Semaphore等也会变。我们使用WaitForSingleObject函数等待信号,如果要等待多个信号可以使用WaitForMutipleObject函数。 DWORD WaitForSingleObject( HANDLE hHandle, // 等待对象的句柄 DWORD dwMilliseconds // 等待毫秒数,INFINITE表示无限等待 ); 参数hHandle是一个事件的句柄,第二个参数dwMilliseconds是时间间隔。如果时间是有信号状态返回WAIT_OBJECT_0,如果时间超过dwMilliseconds值但时间事件还是无信号状态则返回WAIT_TIMEOUT。 hHandle可以是下列对象的句柄: Change notification Console input Event Job Memory resource notification Mutex Process Semaphore Thread Waitable timer WaitForSingleObject函数用来检测hHandle事件的信号状态,当函数的执行时间超过dwMilliseconds就返回,但如果参数dwMilliseconds为INFINITE时函数将直到相应时间事件变成有信号状态才返回,否则就一直等待下去,直到WaitForSingleObject有返回直才执行后面的代码。 四、实验步骤 (1)画出程序流程图 (2)编写程序源代码 线程创建: #include "stdafx.h" #include <windows.h> #include "stdio.h"
int i=2;
DWORD WINAPI clientthread(LPVOID lpparam) { int num=(int)lpparam;
i++; printf("%d,%d",num,i); return 0; }
int main(int argc, char* argv[]) { int num=1; HANDLE hthread; DWORD dwthreadid;
hthread=CreateThread(NULL,0,clientthread,(LPVOID)num,0,&dwthreadid); if(hthread==NULL){ printf("fail55\n"); } WaitForSingleObject(hthread,2000); CloseHandle(hthread); return 0; } 线程互斥: #include "stdafx.h" #include "windows.h" #include "stdio.h"
HANDLE hMutex,hConsole;
DWORD WINAPI clientthread1(LPVOID lpparam) { int i; char ch=(char)lpparam; DWORD currentthreadid;
currentthreadid=GetCurrentThreadId(); Sleep(1000); WaitForSingleObject(hMutex,INFINITE); for (i=0;i<=9;i++){ SetConsoleTextAttribute(hConsole,FOREGROUND_RED|FOREGROUND_INTENSITY); printf("%c",ch); SetConsoleTextAttribute(hConsole,FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE); Sleep(300); } ReleaseMutex(hMutex); return 0; } DWORD WINAPI clientthread2(LPVOID lpparam) { int i; char ch=(char)lpparam; DWORD currentthreadid;
currentthreadid=GetCurrentThreadId(); WaitForSingleObject(hMutex,INFINITE); for (i=0;i<=9;i++){ SetConsoleTextAttribute(hConsole,FOREGROUND_GREEN|FOREGROUND_INTENSITY); printf("%c",ch); SetConsoleTextAttribute(hConsole,FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE); Sleep(400); } ReleaseMutex(hMutex); return 0; }
int main(int argc, char* argv[]) { char ch1='A',ch2='B'; HANDLE hthread1,hthread2; DWORD dwthreadid;
hConsole=GetStdHandle(STD_OUTPUT_HANDLE); if (hConsole == INVALID_HANDLE_VALUE) return FALSE;
hMutex=CreateMutex(NULL, FALSE, "PrintMutex"); hthread1=CreateThread(NULL,0,clientthread1,(LPVOID)ch1,0,&dwthreadid); if(hthread1==NULL){ printf("fail55\n"); } hthread2=CreateThread(NULL,0,clientthread2,(LPVOID)ch2,0,&dwthreadid); if(hthread2==NULL){ printf("fail55\n"); } WaitForSingleObject(hthread1,9000); WaitForSingleObject(hthread2,9000); CloseHandle(hMutex); CloseHandle(hthread1); CloseHandle(hthread2);
CloseHandle(hConsole); return 0; }五、实验结果及分析 实验二(下) 进程同步实验 一、实验环境 Windows 2000,VC++ 6.0。 二、实验目的 1.掌握基本的同步与互斥算法,理解读者、写者模型。 2.学习使用Windows 2000/XP中基本的同步对象,掌握相关API的使用方法。 3.了解Windows 2000/XP中多线程的并发执行机制,实现进程的同步。 三、实验原理 1.同步对象 同步对象是指Windows中用于实现同步与互斥的实体,包括信号量(Semaphore)、互斥量(Mutex)、临界区(Critical Section)和事件(Events)等。本实验中使用到信号量、互斥量和临界区三个同步对象。 同步对象的使用步骤: 创建/初始化同步对象。 请求同步对象,进入临界区(互斥量上锁)。 释放同步对象(互斥量解锁)。 这些对象在一个线程中创建,在其他线程中都可以使用,实现同步与互斥。 2.相关API的功能及使用 我们利用Windows SDK提供的API编程实现实验题目要求,而VC中包含有Windows SDK的所有工具和定义。要使用这些API,需要包含堆这些函数进行说明的SDK头文件——最常见的是Windows.h(特殊的API调用还需要包含其他头文件)。 下面给出的是本实验使用到的API的功能和使用方法简单介绍。 (1) CreateThread 功能——创建一个在调用进程的地址空间中执行的线程 格式 HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParamiter, DWORD dwCreationFlags, Lpdword lpThread ); 参数说明 lpThreadAttributes——指向一个LPSECURITY_ATTRIBUTES(新线程的安全性描述符)。 dwStackSize——定义原始堆栈大小。 lpStartAddress——指向使用LPTHRAED_START_ROUTINE类型定义的函数。 lpParamiter——定义一个给进程传递参数的指针。 dwCreationFlags——定义控制线程创建的附加标志。 lpThread——保存线程标志符(32位) (2) CreateMutex 功能——创建一个命名或匿名的互斥量对象 格式 HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCTSTR lpName); 参数说明 lpMutexAttributes——必须取值NULL。 bInitialOwner——指示当前线程是否马上拥有该互斥量(即马上加锁)。 lpName——互斥量名称。 (3) CreateSemaphore 功能——创建一个命名或匿名的信号量对象 格式 HANDLE CreateSemaphore(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, LPCTSTR lpName ); 参数说明 lpSemaphoreAttributes——必须取值NULL。 lInitialCount——信号量的初始值。该值大于0,但小于lMaximumCount指定的最大值。 lMaximumCount——信号量的最大值。 lpName——信号量名称。 (4) WaitForSingleObject 功能——使程序处于等待状态,直到信号量hHandle出现(即其值大于等于1)或超过规定的等待时间 格式 DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds); 参数说明 hHandle——信号量指针。 dwMilliseconds——等待的最长时间(INFINITE为无限等待)。 (5) ReleaseSemaphore 功能——对指定信号量加上一个指定大小的量。成功执行则返回非0值 格式 BOOL ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount, LPLONG lppreviousCount ); 参数说明 hSemaphore——信号量指针。 lReleaseCount——信号量的增量。 lppreviousCount——保存信号量当前值。 (6) ReleaseMutex 功能——打开互斥锁,即把互斥量加1。成功调用则返回0 格式 BOOL ReleaseMutex(HANDLE hMutex); 参数说明 hMutex——互斥量指针。 (7) InitializeCriticalSection 功能——初始化临界区对象 格式 VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection); 参数说明 lpCriticalSection——指向临界区对象的指针。 (8) EnterCriticalSection 功能——等待指定临界区对象的所有权 格式 VOID enterCriticalSection(LPCRITICAL_SECTION lpCriticalSection); 参数说明 lpCriticalSection——指向临界区对象的指针。 (9) LeaveCriticalSection 功能——释放指定临界区对象的所有权 格式 VOID LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection); 参数说明 lpCriticalSection——指向临界区对象的指针。 四、实验步骤 (1)画出程序流程图 (2)编写程序源代码 // semaphore.cpp : Defines the entry point for the console application. //
#include "stdafx.h" #include "windows.h" #include "stdio.h"
HANDLE hConsole; HANDLE Wsemaphore; HANDLE Rsemaphore; long * Wsemaphorecount; long * Rsemaphorecount; int data=0;
DWORD WINAPI clientthread1(LPVOID lpparam) { for (;;){ Sleep(1000); ReleaseSemaphore(Wsemaphore,-1,Wsemaphorecount); WaitForSingleObject(Wsemaphore,INFINITE); data++; ReleaseSemaphore(Rsemaphore,1,Rsemaphorecount); } return 0; } DWORD WINAPI clientthread2(LPVOID lpparam) { for (;;){ ReleaseSemaphore(Rsemaphore,-1,Rsemaphorecount); WaitForSingleObject(Rsemaphore,INFINITE); SetConsoleTextAttribute(hConsole,FOREGROUND_RED|FOREGROUND_INTENSITY); printf("DATA = "); SetConsoleTextAttribute(hConsole,FOREGROUND_GREEN|FOREGROUND_INTENSITY); printf("%d\n",data); ReleaseSemaphore(Wsemaphore,1,Wsemaphorecount); } SetConsoleTextAttribute(hConsole,FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE); return 0; }
int main(int argc, char* argv[]) { HANDLE hthread1,hthread2; DWORD dwthreadid;
hConsole=GetStdHandle(STD_OUTPUT_HANDLE); if (hConsole == INVALID_HANDLE_VALUE) return FALSE;
Wsemaphore=CreateSemaphore(NULL, 1,1, "WSemaphore"); Rsemaphore=CreateSemaphore(NULL, 0,1, "RSemaphore"); hthread1=CreateThread(NULL,0,clientthread1,NULL,0,&dwthreadid); if(hthread1==NULL){ printf("fail55\n"); } hthread2=CreateThread(NULL,0,clientthread2,NULL,0,&dwthreadid); if(hthread2==NULL){ printf("fail55\n"); } WaitForSingleObject(hthread1,INFINITE); WaitForSingleObject(hthread2,INFINITE); CloseHandle(Wsemaphore); CloseHandle(Rsemaphore); CloseHandle(hthread1); CloseHandle(hthread2);
return 0; }五、实验结果及分析
|