windows编程 第11课 win32线程

线程基础

        windows线程是可以执行的代码的实例。系统是以线程为单位调度程序 。一个程序当中可以有多个线程,实现多任务的处理。

        windows线程的特点:

                每个线程都具有一个ID

                每个线程都具有自己的内存栈(其余内存空间都是共享的)

                同一进程中的线程使用同一个地址空间

        线程的调度

                操作系统将CPU的执行时间划分成时间片,依次根据时间片执行不同的线程。

                线程轮询:线程A->线程B-> 线程A。。。。

创建一个线程:

HANDLE
WINAPI
CreateThread(
    _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, // 安全属性
    _In_ SIZE_T dwStackSize,
    _In_ LPTHREAD_START_ROUTINE lpStartAddress, // 线程处理函数的函数地址
    _In_opt_ __drv_aliasesMem LPVOID lpParameter, // 传递给线程处理函数的参数
    _In_ DWORD dwCreationFlags, // 线程的创建方式 1) 0 立即执行方式,创建后立即执行 2) CREATE_SUSPEND 挂起方式,创建后先处于休眠,唤醒后继续执行
    _Out_opt_ LPDWORD lpThreadId // 创建成功,返回线程的ID
    );

定义线程处理函数

typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)(
    LPVOID lpThreadParameter
    );

线程挂起

       

DWORD SuspendThread(HANDLE hThread) // handle to thread

唤醒线程     

   DWORD ResumeThread( HANDLE hThread );

结束指定线程(线程外结束)

BOOL TerminateThread(
    HANDLE hThread, 
    DWORD dwExitCode 
);

结束函数所在的线程(线程内结束)

VOID ExitThread( DWORD dwExitCode)

线程相关操作

// 获取当前线程的ID
GetCurrentThreadId()

// 获取当前线程的句柄
GetCurrentThread()

// 等候单个句柄有信号(很重要)
// 必须是可等候句柄
VOID WaitForSingleObject(
    HANDLE handle, // 句柄BUFF的地址
    DWORD dwMilliseconds // 等候时间ms级别 INFINITE无限时间
)

// 等候多个句柄有信号
DWORD WaitForMultipleObjects(
    DWORD nCount, // 句柄数量
    CONST HANDLE *lpHandles, // 句柄的地址
    BOOL bWaitAll, // 等候方式 是否等待全部有信号还是非等待全部有信号就结束等候
    DWORD dwMilliseconds // 等候时间 INFINITE 永无超时
)

线程同步

        原因:操作临界资源,多个线程同时操作。

        原子锁

         相关问题:多个线程对同一个数据进行原子操作,会产生结果丢失。

        比如执行++运算时。

        错误代码分析:当线程A执行g_value++时,如果线程切换时间正好是在线程A将值保存到g_value之前,线程B继续执行g_value++,那么当线程A再次被切换回来之后,会将原来线程A保存的值保存到g_value上,线程B进行的加法操作被覆盖。

        使用原子锁函数,这里注意,性能会牺牲。

                InterlockedIncrement

                InterlockedDecrement

                InterlockedCompareExchange

                InterlockedExchange

        ....

        原子锁的实现:直接对数据所在的内存操作,并且在任何一个瞬间只能有一个线程访问,也就是说直接操作内存,不涉及寄存器

        1) 尝试对数据所在内存进行加锁(独占锁)

        2) 加锁成功后操作数据, 加锁失败会堵塞,这也是为什么执行速度慢一些,但是会保证结果的正确性!

示例代码:

#include <iostream>
#include <Windows.h>

long g_value = 0;
DWORD WINAPI PTHREAD_START_ROUTINE_Impl(LPVOID lpThreadParameter)
{
	for (int i = 0; i < 100000; i++)
	{
		InterlockedIncrement(&g_value);
		// g_value++;
	}
	return 0;
}

DWORD WINAPI PTHREAD_START_ROUTINE_Impl2(LPVOID lpThreadParameter)
{
	for (int i = 0; i < 100000; i++)
	{
		InterlockedIncrement(&g_value);
		// g_value++;
	}
	return 0;
}


int main()
{
	LPDWORD threadid1 = 0, threadid2 = 0;
	HANDLE handles[2];
	handles[0] = CreateThread(NULL, 1024, PTHREAD_START_ROUTINE_Impl, 0, 0, threadid1);
	handles[1] = CreateThread(NULL, 1024, PTHREAD_START_ROUTINE_Impl2, 0, 0, threadid2);

	// 需要保证主线程不退出
	WaitForMultipleObjects(2, handles, true, INFINITE);
	printf("wait over!   %d\n", g_value);

	getchar();

	return 0;
}

    原子锁的难点:

        缺点:API函数太多。局限性大,只针对操作符号。

        优点:效率相对最高

 互斥:多线程下代码或资源的共享使用(临界资源)

        特性:1. 任何一个时间点只能有一个线程拥有互斥

                   2. 任何线程都没有互斥,则有信号,否则无信号

                   3. 谁先等互斥谁就有拥有互斥

             互斥的使用

                       

 示例代码:

#include <iostream>
#include <Windows.h>

long g_value = 0;
// 定义一个互斥句柄,可等候句柄
HANDLE mutexHandle = NULL;
HANDLE threads[2];

DWORD CALLBACK PTHREAD_START_ROUTINE_Impl(LPVOID lpThreadParameter)
{
	for (int i = 0; i < 100000; i++)
	{
		// 加独占锁
		WaitForSingleObject(mutexHandle, INFINITE);
		g_value++;
		ReleaseMutex(mutexHandle);
	}
	return 0;
}

DWORD CALLBACK PTHREAD_START_ROUTINE_Imp2(LPVOID lpThreadParameter)
{
	for (int i = 0; i < 100000; i++)
	{
		// 加独占锁
		WaitForSingleObject(mutexHandle, INFINITE);
		g_value++;
		ReleaseMutex(mutexHandle);
	}
	return 0;
}

int main()
{
	// 创建互斥
	// 一般在主线程进行创建
	// 主线程不能互斥
	mutexHandle = CreateMutex(NULL, false, "testMutex");
	LPDWORD id1 = 0, id2 = 0;


	threads[0] = CreateThread(0, 1024, PTHREAD_START_ROUTINE_Impl, 0, 0, id1);
	threads[1] = CreateThread(0, 1024, PTHREAD_START_ROUTINE_Imp2, 0, 0, id2);

	WaitForMultipleObjects(2, threads, true, INFINITE);
	printf("END! %d \n" , g_value);

	CloseHandle(mutexHandle);

	getchar();

	

	return 0;
}

示例2:

#include <iostream>
#include <Windows.h>

long g_value = 0;
// 定义一个互斥句柄,可等候句柄
HANDLE mutexHandle = NULL;
HANDLE threads[2];

DWORD CALLBACK PTHREAD_START_ROUTINE_Impl(LPVOID lpThreadParameter)
{
	while (1)
	{
		// 加独占锁
		WaitForSingleObject(mutexHandle, INFINITE);
		for (int i = 0; i < 9; ++i)
		{
			printf("*");
			Sleep(100);
		}
		printf("\n");
		ReleaseMutex(mutexHandle);
	}
	return 0;
}

DWORD CALLBACK PTHREAD_START_ROUTINE_Imp2(LPVOID lpThreadParameter)
{
	while (1)
	{
		// 加独占锁
		WaitForSingleObject(mutexHandle, INFINITE);
		for (int i = 0; i < 9; ++i)
		{
			printf("-");
			Sleep(100);
		}
		printf("\n");
		ReleaseMutex(mutexHandle);
	}
	return 0;
}

int main()
{
	// 创建互斥
	// 一般在主线程进行创建
	// 主线程不能互斥
	mutexHandle = CreateMutex(NULL, false, "testMutex");
	LPDWORD id1 = 0, id2 = 0;


	threads[0] = CreateThread(0, 1024, PTHREAD_START_ROUTINE_Impl, 0, 0, id1);
	threads[1] = CreateThread(0, 1024, PTHREAD_START_ROUTINE_Imp2, 0, 0, id2);

	WaitForMultipleObjects(2, threads, true, INFINITE);
	printf("END! %d \n" , g_value);

	CloseHandle(mutexHandle);

	getchar();

	

	return 0;
}

事件

相关问题:线程之间的通知的问题

创建线程

HANDLE
WINAPI
CreateEventA(
    _In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes, // 安全属性 设置为0即可
    _In_ BOOL bManualReset, // 手工重置
    _In_ BOOL bInitialState, // 初始创建 true有信号
    _In_opt_ LPCSTR lpName \\ 事件名
    );

事件的其它操作

 

信号量

        类似于事件,解决通知的相关问题。但提供一个计数器,可以设置次数。

         创建信号量

        

HANDLE
WINAPI
CreateSemaphoreA(
    _In_opt_ LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // 安全属性
    _In_     LONG lInitialCount, // 初始化值,每次等待只要大于0就有信号,通过一次就减一
    _In_     LONG lMaximumCount, // 最大值
    _In_opt_ LPCSTR lpName // 信号量名称
    );

        信号量其它操作

 对比以上,原子锁、互斥、事件、信号量如何选择呢?

原子锁和互斥主要解决临界资源问题,互斥应用更广,但是效率稍微低。

事件和信号量都解决线程同步问题,但是信号量具有计数功能,可以对资源数量进行控制,比如限制http服务器的访问客户数量。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值