CreateEvent

创建线程:

  1. pThreadAttrivutes:指向SECURITY_ATTRIBUTES的指针,用于定义新线程的安全属性,一般设置成NULL;
  2. dwStackSize:分配以字节数表示的线程堆栈的大小,默认值是0;
  3. lpStartAddress:指向一个线程函数地址。每个线程都有自己的线程函数,线程函数是线程具体的执行代码;
  4. lpParameter:传递给线程函数的参数;
  5. dwCreationFlags:表示创建线程的运行状态,其中CREATE_SUSPEND表示挂起当前创建的线程,而0表示立即执行当前创建的进程;
  6. lpThreadID:返回新创建的线程的ID编号;
  7. 如果函数调用成功,则返回新线程的句柄。

     //Definition
     HANDLE CreateThread(
                         LPSECURITY_ATTRIBUTES lpThreadAttributes,
                         DWORD dwStackSize,
                         LPTHREAD_START_ROUTINE lpStartAddress,
                         LPVOID lpParameter,
                         DWORD dwCreationFlags,
                         LPDWORD lpThreadID
                        );
    
     //Example
     #include "windows.h"
    
     DWORD WINAPI ThreadFunc(LPVOID);
    	
     int main(array<System::String^>^ args)
     {		
         HANDLE hThread;
         DWORD  threadId;
    	
         hThread = CreateThread(NULL, 0, ThreadFunc, 0, 0, &threadId); // 创建线程
         printf("我是主线程, pid = %d\n", GetCurrentThreadId());  //输出主线程pid
         Sleep(2000);
    	
         if (hThread == NULL) {
             std::cerr << "CreateThread failed (error " << GetLastError() << ")" << std::endl;
             return 1;
         }
    
         WaitForSingleObject(hThread, INFINITE);
         CloseHandle(hThread);
    
         return 0;
     }
    	
     DWORD WINAPI ThreadFunc(LPVOID p)
     {
         printf("我是子线程, pid = %d\n", GetCurrentThreadId());   //输出子线程pid
         return 0;
     }
    

WaitForSingleObject

调用WaitForSingleObject函数等待所创建线程的运行结束。

  1. hHandle:指定对象或时间的句柄;
  2. dwMilliseconds:等待时间,以毫秒为单位,当超过等待时间时,此函数返回。如果参数设置为0,则该函数立即返回;如果设置成INFINITE,则该函数直到有信号才返回。

     DWORD WaitForSingleObject(
                               HANDLE hHandle,
                               DWORD dwMilliseconds
                              );
    

线程同步

一般情况下需要创建多个线程来提高程序的执行效率,但是多个线程同时运行的时候可能调用线程函数,在多个线程同时对一个内存地址进行写入操作,由于CPU时间调度的问题,写入的数据会被多次覆盖,所以要使线程同步。

线程和线程句柄

线程和线程句柄(Handle)不是一个东西,线程是在cpu上运行的,线程句柄是一个内核对象。我们可以通过句柄来操作线程,但是线程的生命周期和线程句柄的生命周期不一样的。线程的生命周期就是线程函数从开始执行到return,线程句柄的生命周期是从CreateThread返回到你CloseHandle()。所有的内核对象(包括线程Handle)都是系统资源,用了要还的,也就是说用完后一定要closehandle关闭之,如果不这么做,你系统的句柄资源很快就用光了。CloseHandel(ThreadHandle)只是关闭了一个线程句柄对象,表示我不再使用该句柄,即不对这个句柄对应的线程做任何干预了。并没有结束线程。

CloseHandle

理论上不能重复关闭句柄,在debug模式下会报错,但是在release模式下不会报错,关闭句柄不会把句柄设置为0。

MFC多线程函数,AfxBeginThread

UINT  ThreadFunc(LPVOID  lParam)
{
	while (true) {
		cout << "子线程" << endl;
		Sleep(1000);
	}
}	

int main()
{
	AfxBeginThread(ThreadFunc, NULL);  
	Sleep(10000);
    return 0;
}

中止线程

  1. 线程函数返回(最好使用该方法)。
  2. 同一个进程或另一个进程中的线程调用TerminateThread函数(应避免使用该方法)。
  3. 通过调用ExitThread函数,线程将自行撤消(最好不使用该方法)。
  4. ExitProcess和TerminateProcess函数也可以用来终止线程的运行(应避免使用该方法)。

中止线程设计规范

始终都应该将线程设计成这样的形式,即当想要线程终止运行时,它们就能够返回。这是确保所有线程资源被正确地清除的唯一办法。如果线程能够返回,就可以确保下列事项的实现:

  1. 在线程函数中创建的所有C++对象均将通过它们的析构函数进行释放。
  2. 操作系统将正确地释放线程堆栈使用的内存。
  3. 系统将线程的退出代码(在线程的内核对象中维护)设置为线程函数的返回值。
  4. 系统将递减线程内核对象的使用计数。