基础知识
程序:可执行文件(exe),物理文件。
进程:由程序运行之后,就产生了进程,一般一个程序产生一个进程,当然也有一个程序产生多个进程的情况(linux常见),进程相当于一个容器,容器中运行着线程,并且提供线程所需的资源(cpu,IO,内存…)。一个进程中,最少有一个线程(主线程,main启动),每个进程中,都有独立的32位地址空间,产生多进程的函数CreateProcess,这里不多介绍。
线程:是程序运行的基本单元,本质是完成一个任务。进程中可以有多线程,其中main启动主线程。进程结束,其中的线程都将结束;主线程结束,进程结束。线程退出有三种方式:1.自然结束,任务完成,线程结束;2.线程内部强制结束,给定一定的条件,使得线程函数退出;3.线程外部强制结束,TerminateThread在线程外部,强制退出线程。
多线程并行执行程序的机理:使用CPU,将CPU分为很多细小时间片,每个线程在他的时间片内运行,运行完之后,切换到下一个线程,依次切换,由于切换的时间非常的短,使人感觉像是多个线程同时运行。单线程程序的运行效率一定是高于多线程,所以当有多个任务需要并行执行时才用到多线程。
下面就是WIN32里关于线程的基础基础回顾,关于C++ 11新特性中,已经可以使用std::thread来创建线程的方法,具体可以参考C++多线程参考
多线程函数介绍
创建线程`:CreateThread(); _beginThread();
HANDLE WINAPI CreateThread(
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, //安全属性,一般设置为0
_In_ SIZE_T dwStackSize, //栈大小,线程能够使用到进程的多少栈空间,一般为0
_In_ LPTHREAD_START_ROUTINE lpStartAddress, //线程函数(任务函数),函数指针
_In_opt_ LPVOID lpParameter, //线程参数
_In_ DWORD dwCreationFlags, //创建的标志
_Out_opt_ LPDWORD lpThreadId //线程ID,输出参数,不要的话,可以给0
);
其他:
sleep1.休眠当前线程,毫秒为单位;2.当前线程放弃执行权限。
SuspendThread 挂起线程
ResumeThread 唤醒线程
线程同步的方式
线程同步是为了让公共资源有序的访问(防止同一个资源被多个线程同时访问)
方式1——原子锁
//例:线程1和线程2有可能输出的值是一样的
unsigned int i=1;
printf("%d ",i++); //线程1
printf("%d ",i++); //线程2
//正确的写法
printf("%d",InterlockedIncrement(&i);
相关的函数:InterlockedIncrement(); InterlockedDecrement(); InterlockedAdd(); InterlockedAnd(); InterlockedOr();
方式2——关键代码区(临界区)
只能用于同一个进程的多线程同步,效率高,速度快
与之相关的内容:
CRITICAL_SECTION
相关函数
InitializeCriticalSection
EnterCriticalSection
LeaveCriticalSection
DeleteCriticalSection
例子
//举一个卖票的例子,也就是从100开始自减
#include <Windows.h>
#include <stdio.h>
struct ThreadPar
{
int t;
};
//卖票
int ticket = 100; //总票数
//同步1, 关键代码段
CRITICAL_SECTION cs;
DWORD WINAPI SelltickThreadProc1(LPVOID lpParameter);
DWORD WINAPI SelltickThreadProc2(LPVOID lpParameter);
DWORD WINAPI SelltickThreadProc3(LPVOID lpParameter);
int main(int argc, _TCHAR* argv[])
{
//初始化关键代码段
InitializeCriticalSection(&cs);
HANDLE handle = CreateThread(0, 0, SelltickThreadProc1, 0, 0, 0);
CloseHandle(handle);
handle = CreateThread(0, 0, SelltickThreadProc2, 0, 0, 0);
CloseHandle(handle);
Sleep(1000);
return 0;
}
DWORD WINAPI SelltickThreadProc1(LPVOID lpParameter)
{
while (1)
{
//进入关键代码段,加锁, 这是个阻塞的函数,抢到cs,函数立马返回,其他线程将等待
EnterCriticalSection(&cs);
if (ticket <= 0)
{
LeaveCriticalSection(&cs);
break;
}
printf("thread 1 sell Ticket %d\n", ticket--); //卖了76,ticket = 75
//离开关键代码段,离开之后,其他线程可以抢cs
LeaveCriticalSection(&cs);
Sleep(1);
}
return 0;
}
DWORD WINAPI SelltickThreadProc2(LPVOID lpParameter)
{
while (1)
{
EnterCriticalSection(&cs);
if (ticket <= 0)
{
LeaveCriticalSection(&cs);
break;
}
printf("thread 2 sell Ticket %d\n", ticket--);
LeaveCriticalSection(&cs);
Sleep(1);
}
return 0;
}
方式3——互斥对象
可以用于多进程中的多线程同步防止同一个程序运行多次Mutex;
相关函数
HANDLE WINAPI CreateMutex(
_In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes,
_In_ BOOL bInitialOwner, //创建该对象的线程是否拥有它
_In_opt_ LPCTSTR lpName //互斥对象的名
);
WaitForSingleObject 等待一个对象
例子
#include <Windows.h>
#include <stdio.h>
struct ThreadPar
{
int t;
};
//卖票
int ticket = 100; //总票数
//同步1, 互斥对象
HANDLE hMutex;
DWORD WINAPI SelltickThreadProc1(LPVOID lpParameter);
DWORD WINAPI SelltickThreadProc2(LPVOID lpParameter);
int main(int argc, _TCHAR* argv[])
{
//创建互斥对象
//hMutex = CreateMutex(0, 1, 0);
//ReleaseMutex(hMutex);
hMutex = CreateMutex(0, 0, 0);
HANDLE handle = CreateThread(0, 0, SelltickThreadProc1, 0, 0, 0);
CloseHandle(handle);
handle = CreateThread(0, 0, SelltickThreadProc2, 0, 0, 0);
CloseHandle(handle);
Sleep(1000);
return 0;
}
DWORD WINAPI SelltickThreadProc1(LPVOID lpParameter)
{
while (1)
{
WaitForSingleObject(hMutex, INFINITE);
if (ticket <= 0)
{
ReleaseMutex(hMutex);
break;
}
printf("thread 1 sell Ticket %d\n", ticket--); //卖了76,ticket = 75
ReleaseMutex(hMutex);
Sleep(1);
}
return 0;
}
DWORD WINAPI SelltickThreadProc2(LPVOID lpParameter)
{
while (1)
{
WaitForSingleObject(hMutex, INFINITE);
if (ticket <= 0)
{
ReleaseMutex(hMutex);
break;
}
printf("thread 2 sell Ticket %d\n", ticket--);
ReleaseMutex(hMutex);
Sleep(1);
}
return 0;
}
方式3——事件
相关函数
HANDLE WINAPI CreateEvent(
_In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes,
_In_ BOOL bManualReset, //手动重置,还是自动重置 (重置为无信号状态)
_In_ BOOL bInitialState, //初始的信号状态
_In_opt_ LPCTSTR lpName
);
ResetEvent //将事件对象设置为无信号状态
SetEvent //将事件对象设置为有信号状态
例子
#include <Windows.h>
#incldue <stdio.h>
struct ThreadPar
{
int t;
};
//卖票
int ticket = 100; //总票数
//同步1, 事件对象
HANDLE hEvent;
DWORD WINAPI SelltickThreadProc1(LPVOID lpParameter);
DWORD WINAPI SelltickThreadProc2(LPVOID lpParameter);
#if 0
int main(int argc, _TCHAR* argv[])
{
//创建了一个手动重置的事件,初始是无信号状态
hEvent = CreateEvent(0, TRUE, FALSE, 0);
SetEvent(hEvent); //设置为有信号
HANDLE handle = CreateThread(0, 0, SelltickThreadProc1, 0, 0, 0);
CloseHandle(handle);
handle = CreateThread(0, 0, SelltickThreadProc2, 0, 0, 0);
CloseHandle(handle);
Sleep(1000);
return 0;
}
DWORD WINAPI SelltickThreadProc1(LPVOID lpParameter)
{
while (1)
{
WaitForSingleObject(hEvent, INFINITE); //hEvent 有信号才返回
//对于手动重置的事件对象, 现在是有信号,还是无信号状态? 有信号状态 , 需要调用ReSetEvent 设为无信号状态
ResetEvent(hEvent);
if (ticket <= 0)
{
SetEvent(hEvent);
break;
}
printf("thread 1 sell Ticket %d\n", ticket--); //卖了76,ticket = 75
SetEvent(hEvent); //再将事件设置为有信号状态,其他线程可以抢到该事件
Sleep(1);
}
return 0;
}
DWORD WINAPI SelltickThreadProc2(LPVOID lpParameter)
{
while (1)
{
WaitForSingleObject(hEvent, INFINITE);
ResetEvent(hEvent);
if (ticket <= 0)
{
SetEvent(hEvent);
break;
}
printf("thread 2 sell Ticket %d\n", ticket--);
SetEvent(hEvent);
Sleep(1);
}
return 0;
}
#endif
int main(int argc, _TCHAR* argv[])
{
//创建了一个自动重置的事件,初始是有信号状态
hEvent = CreateEvent(0, FALSE, TRUE, 0);
HANDLE handle = CreateThread(0, 0, SelltickThreadProc1, 0, 0, 0);
CloseHandle(handle);
handle = CreateThread(0, 0, SelltickThreadProc2, 0, 0, 0);
CloseHandle(handle);
Sleep(10000000);
return 0;
}
DWORD WINAPI SelltickThreadProc1(LPVOID lpParameter)
{
while (1)
{
WaitForSingleObject(hEvent, INFINITE); //hEvent 有信号才返回
//对于自动重置的事件对象, 现在是有信号,还是无信号状态? 有无信号状态 ,当调用完waitfor。。之后,就变成了无信号状态
if (ticket <= 0)
{
SetEvent(hEvent);
break;
}
printf("thread 1 sell Ticket %d\n", ticket--); //卖了76,ticket = 75
SetEvent(hEvent); //再将事件设置为有信号状态,其他线程可以抢到该事件
Sleep(1);
}
return 0;
}
DWORD WINAPI SelltickThreadProc2(LPVOID lpParameter)
{
while (1)
{
WaitForSingleObject(hEvent, INFINITE);
if (ticket <= 0)
{
SetEvent(hEvent);
break;
}
printf("thread 2 sell Ticket %d\n", ticket--);
SetEvent(hEvent);
Sleep(1);
}
return 0;
}