多线程知识回顾


基础知识

程序:可执行文件(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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值