windows 线程一般用法

本文详细介绍了Windows环境下线程的使用,包括win32线程的CreateThread和_beginthreadex,MFC的AfxBeginThread以及C++11的std::thread和std::async。讨论了线程的优先级设置、线程通信和同步机制,如临界区、事件、互斥量和信号量,并提到了线程池的概念和实现。
摘要由CSDN通过智能技术生成

目录

一.  线程说明

        1. 概念

        2. 线程与进程

        3. 执行

二.  windows下的各种线程

        1. win32线程

                (1) CreateThread

                (2) _beginthreadex

                (3) 如何选择使用哪个线程函数

        2. mfc线程

                (1) 工作线程:AfxBeginThread

                (2) 界面线程 

                (3)  使用说明

        3. C++11 线程

                (1)  std:: thread

                (2)  std::async

        4. 总结

三.  线程优先级

        1. 概念

        2. 实现

四. 线程通信

        1. 概念

        2. 自定义消息

        3. C++11通信

五. 线程同步

        1. 概念

        2. 临界区

        3. 事件

        4. 互斥量

                (1) 互斥锁

                (2) lock_guard

                (3) std::unique_lock

        5. 信号量

                (1) 说明

                 (2) wait

                (3) notify_xx

                (4) 实现

六.  线程池(C++11)

        1. 概念

        2. 同步任务队列 

        3. 线程池

        4. 实现


一.  线程说明

        1. 概念

                线程是进程中的一个实体,是被系统独立分配和调度的基本单位。也有说,线程是CPU可执行调度的最小单位。

        2. 线程与进程

                 线程是进程的可执行单元,是计算机分配CPU机时的基本单元。一个进程可以包含一个或多个线程,进程是通过线程去执行代码的。同一个进程的多个线程共享该进程的资源和操作系统分配给该进程的内存空间。每个进程必须有一个主线程,主线程退出之后该进程也就退出了。一个进程的主线程是由系统创建的。

        3. 执行

                   在单CPU中,表面上看好像是多个进程中的多个线程共同执行,实际上是操作系统根据调度规则、依次的 将一个一个的线程可执行代码加载进CPU中执行的;即,CPU在同一时刻只能执行一段代码,由于CPU的频率非常快,迅速的在各个线程间进行切换,所以给人的感觉就好像是多个线程共同执行。

                    不过在多核CPU的电脑中,确实是多个线程共同执行……

                    所以,通过多线程,一个进程的应用程序可以充分利用CPU资源,但什么事情都是物极必反,如果线程开的太多,系统就会增加额外的开销去进行线程的调度,反而降低了CPU的使用效率

二.  windows下的各种线程

        1. win32线程

                (1) CreateThread

                        具体的函数使用可以配合msdn,这里不作函数说明,后面的都是如此。下面只说下线程回调函数:只能是全局或静态成员函数。来看看具体实现

void CthreadTestDlg::DoThread(void* pvPrm)
{
	CthreadTestDlg* pobjThreadTest = static_cast<CthreadTestDlg*>(pvPrm);
	pobjThreadTest->RunThread();
}

void CthreadTestDlg::RunThread()
{
	int iNum = 0;
	while (100 > iNum)
	{
		printf("thread num: %d \n", iNum++);
	}
}

void CthreadTestDlg::OnBnClickedBtnWin32()
{
	if (nullptr == m_hThread)
		m_hThread = CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)DoThread, (LPVOID)this, 0, 0);
}

... ...

//清理
if (nullptr != m_hThread) CloseHandle(m_hThread);

                (2) _beginthreadex

                        还是要说下线程回调函数:全局或静态成员函数,且需要添加_stdcall限制。下面看看实现:

unsigned _stdcall CthreadTestDlg::DoThread2(void* pvPrm)
{
	CthreadTestDlg* pobjThreadTest = static_cast<CthreadTestDlg*>(pvPrm);
	pobjThreadTest->RunThread();

	return 0;
}

void CthreadTestDlg::RunThread()
{
	int iNum = 0;
	while (100 > iNum)
	{
		printf("thread num: %d \n", iNum++);
	}
}

void CthreadTestDlg::OnBnClickedBtnWin322()
{
	if (nullptr == m_hThread2)
		m_hThread2 = (HANDLE)_beginthreadex(nullptr, 0, DoThread2, this, 0, 0);
}

... ...

//清理
if (nullptr != m_hThread2) CloseHandle(m_hThread2);

                (3) 如何选择使用哪个线程函数

                        C运行库中有许多全局变量,如错误码等。多个线程同时操作这些全局变量,就会出错(没同步)。为了解决未同步问题,微软想出了个办法,就是给每个线程划分了自己存全局变量的空间,TLS(thread local storage)。这样,各用各的,就不会出错了。奇怪的是,CreateThread压根没TLS的概念,就不会创建这个内存。当在CreateThread创建的线程中要读写那些全局变量时,发现没有TLS,就会创建一个给自己用。可恨的是,CloseHandle中不会释放TLS。这样,内存就泄漏了。所以,为了保险起见,创建线程使用C运行库中的_beginthreadex

        2. mfc线程

                (1) 工作线程:AfxBeginThread

                        本质上是对CreateThread进行封装,且AfxBeginThread可设置线程优先级,同时对比win32线程,不需要CloseHandle。线程回调函数:全局或静态成员变量。下面看看实现:

UINT CthreadTestDlg::DoThreadAfx(void* pvPrm)
{
	CthreadTestDlg* pobjThreadTest = static_cast<CthreadTestDlg*>(pvPrm);
	pobjThreadTest->RunThread();

	return 0;
}

void CthreadTestDlg::RunThread()
{
	int iNum = 0;

	while (100 > iNum)
	{
		printf("thread num: %d \n", iNum++);
	}
}

//AfxBeginThread不需要CloseHandle
void CthreadTestDlg::OnBnClickedBtnMfc()
{
	if (nullptr == m_pThreadAfx)
		m_pThreadAfx = AfxBeginThread(DoThreadAfx, this, THREAD_PRIORITY_NORMAL);//可以设置线程优先级
}

                (2) 界面线程 

                        ① 从 CWinThread 类派生自己的子类:CUIThreadApp;
                        ② 重载 InitInstance(必须重载) 与 ExitInstance(可选重载函数),系统帮我们重载了,这一步可以省略;
                        ③  在 InitInstance 函数中进行界面的创建,且要return FALSE, 否则线程退出不了;
                        ④ 开启界面线程:AfxBeginThread(RUNTIME_CLASS(CUIThread))。            

// 继承了CWindThread派生类CUIThread

... ...

#define DLG_MODAL (1)
BOOL CUIThread::InitInstance()
{
#if DLG_MODAL //模态对话框的创建
	CDlgTest objDlgTest;
	objDlgTest.DoModal();

	return FALSE;
#elif //非模态对话框的创建
	m_pobjDlgTest = new CDlgTest;
	m_pobjDlgTest->Create(IDD_DLG_THREAD, nullptr);
	m_pobjDlgTest->ShowWindow(SW_SHOW);

	m_pobjDlgTest->RunModalLoop();

	return FALSE;
	//return TRUE; //这里return true,则线程不会退出
#endif

	return TRUE;
}

int CUIThread::ExitInstance()
{
#if DLG_MODAL

#elif
	if (m_pobjDlgTest)
	{
		m_pobjDlgTest->DestroyWindow();
		delete m_pobjDlgTest;
	}
#endif

	return CWinThread::ExitInstance();
}

... ...

AfxBeginThread(RUNTIME_CLASS(CUIThread));

                (3)  使用说明

                        虽然mfc定义了界面线程,但实际项目中还是不建议在线程里操作界面,原因:

                        ① 同一进程中的多线程是资源共享的,在多线程中操作界面会发生意想不到的结果,特别是像窗口的销毁等。

                        ② 我们在设计代码时,是希望界面层与业务层是弱耦合关系,以实现单层模块的"高内聚",所以如果线程需要与界面层通信,可以使用PostMessage进行关联

        3. C++11 线程

   

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值