一、创建线程
HANDLE WINAPI CreateThread( _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, //新线程安全属性 _In_ SIZE_T dwStackSize, //线程初始化时地址空间大小 _In_ LPTHREAD_START_ROUTINE lpStartAddress, //线程函数地址 _In_opt_ LPVOID lpParameter, //传递给线程的命令行参数 _In_ DWORD dwCreationFlags, //线程创建后是否立即运行 _Out_opt_ LPDWORD lpThreadId //线程ID号 );
// create_thread.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <windows.h> DWORD WINAPI myfun1( LPVOID lpParameter ); DWORD WINAPI myfun2( LPVOID lpParameter ); int _tmain(int argc, _TCHAR* argv[]) { HANDLE h1, h2; h1 = ::CreateThread(NULL, 0, myfun1, NULL, 0, NULL); printf("线程1开始运行!\r\n"); h2 = ::CreateThread(NULL, 0, myfun2, NULL, 0, NULL); printf("线程2开始运行!\r\n"); ::CloseHandle(h1); ::CloseHandle(h2); if (getchar() == 'q') { return 0; } else { ::Sleep(100); } return 0; } DWORD WINAPI myfun1( LPVOID lpParameter ) { printf("线程1正在运行!\r\n"); return 0; } DWORD WINAPI myfun2( LPVOID lpParameter ) { printf("线程2正在运行!\r\n"); return 0; }
二、多线程同步
同步有多种方法,如临界区对象、事件对象、互斥对象。
临界区对象:
初始化临界区
void WINAPI InitializeCriticalSection( _Out_ LPCRITICAL_SECTION lpCriticalSection );
进入临界区函数:
void EnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection );
void LeaveCriticalSection( LPCRITICAL_SECTION lpCriticalSection );
void DeleteCriticalSection( LPCRITICAL_SECTION lpCriticalSection );作用防止内存错误
代码如下:
// create_thread.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <windows.h> DWORD WINAPI myfun1( LPVOID lpParameter ); DWORD WINAPI myfun2( LPVOID lpParameter ); static int a1 = 0; CRITICAL_SECTION Section; int _tmain(int argc, _TCHAR* argv[]) { InitializeCriticalSection(&Section); HANDLE h1, h2; h1 = ::CreateThread(NULL, 0, myfun1, NULL, 0, NULL); printf("线程1开始运行!\r\n"); h2 = ::CreateThread(NULL, 0, myfun2, NULL, 0, NULL); printf("线程2开始运行!\r\n"); ::CloseHandle(h1); ::CloseHandle(h2); ::Sleep(1000); printf("正常退出程序请按'q'\r\n"); if (getchar() == 'q') { DeleteCriticalSection(&Section); } else { return 0; } return 0; } DWORD WINAPI myfun1( LPVOID lpParameter ) { while (1) { EnterCriticalSection(&Section); a1++; if (a1 < 1000) { ::Sleep(1000); printf("线程1正在计数%d\r\n", a1); LeaveCriticalSection(&Section); } else { LeaveCriticalSection(&Section); break; } } return 0; } DWORD WINAPI myfun2( LPVOID lpParameter ) { while (1) { EnterCriticalSection(&Section); a1++; if (a1 < 1000) { ::Sleep(1000); printf("线程2正在计数%d\r\n", a1); LeaveCriticalSection(&Section); } else { LeaveCriticalSection(&Section); break; } } return 0; }其结果如下:
可以知道,在现在多核下,以上建立临界区不能很好的同步。
事件对象:
创建一事件对象
HANDLE WINAPI CreateEvent( _In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes, _In_ BOOL bManualReset, //表示人工重置还是自动重置,true人工,false自动 _In_ BOOL bInitialState, //初始状态,true有状态,false无信号状态 _In_opt_ LPCTSTR lpName );
DWORD WINAPI WaitForSingleObject( _In_ HANDLE hHandle, //表示所等待的事件对象句柄 _In_ DWORD dwMilliseconds //等待时间 );
// create_thread.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <windows.h> DWORD WINAPI myfun1( LPVOID lpParameter ); DWORD WINAPI myfun2( LPVOID lpParameter ); int a1 = 0; HANDLE hevent; DWORD WINAPI WaitForSingleObject( _In_ HANDLE hHandle, //表示所等待的事件对象句柄 _In_ DWORD dwMilliseconds //等待时间 ); HANDLE WINAPI CreateEvent( _In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes, _In_ BOOL bManualReset, //表示人工重置还是自动重置,true人工,false自动 _In_ BOOL bInitialState, //初始状态,true有状态,false无信号状态 _In_opt_ LPCTSTR lpName ); int _tmain(int argc, _TCHAR* argv[]) { HANDLE h1, h2; hevent = ::CreateEvent(NULL, FALSE, false, NULL); ::SetEvent(hevent); h1 = ::CreateThread(NULL, 0, myfun1, NULL, 0, NULL); printf("线程1开始运行!\r\n"); h2 = ::CreateThread(NULL, 0, myfun2, NULL, 0, NULL); printf("线程2开始运行!\r\n"); ::CloseHandle(h1); ::CloseHandle(h2); ::Sleep(20000); return 0; } DWORD WINAPI myfun1( LPVOID lpParameter ) { while (1) { ::WaitForSingleObject(hevent, INFINITE); ::ResetEvent(hevent); if (a1 < 10000) { a1++; ::Sleep(1000); printf("线程1正在计数%d\r\n", a1); ::SetEvent(hevent); } else { ::SetEvent(hevent); break; } } return 0; } DWORD WINAPI myfun2( LPVOID lpParameter ) { while (1) { ::WaitForSingleObject(hevent, INFINITE); ::ResetEvent(hevent); if (a1 < 10000) { a1++; ::Sleep(1000); printf("线程2正在计数%d\r\n", a1); ::SetEvent(hevent); } else { ::SetEvent(hevent); break; } } return 0; }
可以看出用事件对象可以达到同步效果,据其原理是用了WaitForSingleObject,其参数设置成永远等待
互斥对象:
互斥对象不仅可以同步线程,也可以同步进程。
创建互斥对象:
HANDLE WINAPI CreateMutex( _In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes, _In_ BOOL bInitialOwner, //表示该互斥对象的拥有者,true表示创建该互斥对象的线程拥有其所有权,反之,则没 _In_opt_ LPCTSTR lpName //互斥对象的名字 );
// create_thread.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <windows.h> DWORD WINAPI myfun1( LPVOID lpParameter ); DWORD WINAPI myfun2( LPVOID lpParameter ); int a1 = 0; HANDLE hmutex; int _tmain(int argc, _TCHAR* argv[]) { HANDLE h1, h2; hmutex = ::CreateMutex(NULL, FALSE, NULL); h1 = ::CreateThread(NULL, 0, myfun1, NULL, 0, NULL); printf("线程1开始运行!\r\n"); h2 = ::CreateThread(NULL, 0, myfun2, NULL, 0, NULL); printf("线程2开始运行!\r\n"); ::CloseHandle(h1); ::CloseHandle(h2); ::Sleep(20000); return 0; } DWORD WINAPI myfun1( LPVOID lpParameter ) { while (1) { ::WaitForSingleObject(hmutex, INFINITE); if (a1 < 10000) { a1++; ::Sleep(1000); printf("线程1正在计数%d\r\n", a1); ::ReleaseMutex(hmutex); } else { ::ReleaseMutex(hmutex); break; } } return 0; } DWORD WINAPI myfun2( LPVOID lpParameter ) { while (1) { ::WaitForSingleObject(hmutex, INFINITE); if (a1 < 10000) { a1++; ::Sleep(1000); printf("线程2正在计数%d\r\n", a1); ::ReleaseMutex(hmutex); } else { ::ReleaseMutex(hmutex); break; } } return 0; }