线程

Win32线程
WindProc()
{
}
WINMain()
{
CreateWindow();
ShowWindow();
while(GetMessage())
{
TranslateMessage();
DiapatchMessage();
}

}




当窗口回调函数没有结束时,DiapatchMessage()函数是不会返回的,该函数不返回
意味着GetMessage()无法在消息队列里取得下一条消息,取得不了消息就意味着

DiapatchMessage()不会执行即消息不会被分发,消息不会被分发意味着任何消息不会有响应

进程概念


要点1进程是正在执行中的应用程序
要点2进程是资源的集合不活泼
要点3进程间相互独立
要点4同一进程内的线程间数据是共享的


线程的概念


要点1线程是代码的执行单元
要点2线程由两部分组成


线程的回调函数是由操作系统调用的(所有操作系统调用的函数的调用约定为WINAPI即_stdcall调用约定)所以在
线程函数前加WINAPI


为了实现C++的封装思想常把线程函数前加static做为类成员函数


线程的暂停计数
1创建线程时是暂停状态的话暂停计数为1
2创建线程时是运行状态的话暂停计数为0
3SuspendedThread()会使线程的暂停计数加1
4ResumeThread()会使线程的暂停计数减1(但不会出现负值)






关于引用计数(线程内核对象的存在必要条件是引用计数不为零)
1创建线程时是暂停状态的话引用计数为1
2创建线程时是运行状态的话引用计数为2
3创建线程时是暂停状态,然后调用ResumeThread()后引用数为2
4CloseHandle()会使引用计数减1
5线程结束之后引用计数减1


6最好的方式就是线程结束后内核对象一并消失
为了实现这一点我们所采取的方法是线程运行后调用CloseHandle()使引用计数变为1,当
线程结束后内核对象引用计数变为0,操作系统就会回收内核对象

7CloseHandle()(1会使我们的线程句丙关闭,2让引用计数减1)
由于这个原因CloseHandle()调用后,导致无法继续对线程句柄进行操作
使用CloseHandle()也要向delete一样删除后将句丙置空


线程结束


1正常退出(线程函数返回代表正常退出,保证释放了所有的资源)
2非正常退出

1.线程正在运行时应用程序结束(会产生内存泄露)
但是该种情况无关紧要,因为进程结束操作系统会回收所有资源
2.调用TerminateThread()或ExitThread()函数强制杀死线程(强制杀死线程不能保证所有的资源被释放,例如:C++对象)

推荐的线程结束方式为让线程函数正常结束




尽量保证在主线程退出之前其它子线程已经结束(例如,主线程退出时一定会销毁窗口,子线程在窗口中绘图,主线程的窗口资源已经消失,这样会报异常)


怎样保证在主线程退出之前子线程结束




最好在创建线程的时候将线程暂停,然后设置一些线程运行需要的一些变量,然后再让线程运行起来




线程之间互相通信
1 while()等待方式可以实现线程之间通信,但是太浪费CPU资源
2 事件和等待函数的方式(调用等待函数的时候线程会进入睡眠状态,交出时间片来给其他线程执行,提高了系统性能)


1.调用CreateEvent()创建事件
2.事件有两种状态即有信号和无信号;
3.调用等待函数等待事件时只有当事件为有信号或产生错误时等待函数才会返回,否则调用等待函数的那个线程保持睡眠状态
4.对于人工事件和自动事件,等待函数返回之后会有不同的表现
对于人工事件来说将还是有信号,而对于自动事件来说将变为无信号




关于等待函数第二参数的说明

1取值为0代表只是检测信号状态
2取值为INFINITE代表永远等待直到有信号为止
3指定一定的时间间隔等待,如果在时间内事件变为有信号,则返回WAIT_OBJECT_0 ,如果时间结束后始终无信号则函数会返回WAIT_TIMEOUT
4发生了错误返回WAIT_FAILED
3发送消息的方式通信(有灵活性,消息有丢失的可能性)
1操作系统会为每一个线程分配一个消息队列,要想在子线程里处理消息(人为的加入消息循环)即可
1.给子线程发消息用postThreadMessage()其中参数的线程ID可用CreadteThread()的最后一个参数或GetThreadID()得到
在子线程中可以像下面这样处理消息:
if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);


if(msg.message==WM_MSG)
{
AfxMessageBox(_T("hello"));
}
}

2.给主线程发送消息
使用::SendMessage()或::PostMessage()(注意是使用全局函数,因为我们必须明确的知道要发送消息的目标窗口句柄
为了避免错误可使用API的发送消息函数






推荐大家在线程里只做一些幕后工作(不要跟界面元素打交道)
因为主线程调用等待函数,等待函数会使主线程挂起,而在子线程里的窗口操作的一些函数会依赖于主线程的消息循环会产生资源互争
导致死锁,为了避免这样的问题我们可以采取以下方式:(让主线程的消息循环不死)


void CGetTimeDlg::OnClose()
{
m_bRunning = FALSE;//通知子线程退出

if(WAIT_OBJECT_0!=WaitForSingleObject(m_hEvent,0))
{
::PostMessage(this->m_hWnd,WM_CLOSE,0,0);//给自身发消息
return;
}

CDialog::OnClose();
}




关于WM_TIMER消息 
1不准确不能用于准确的定时
2及容易丢失


关于UI线程的等待
由于等待函数会使UI线程睡眠,所以在UI线程中不应出现等待函数
可以建立一个等待线程,当等待线程等待到信号后,发消息给UI线程
关于线程句柄
当线程结束后变为有信号

关于多等待函数的使用
DWORD WaitForMultipleObjects(
  DWORD nCount,             // number of handles in array
  CONST HANDLE *lpHandles,  // object-handle array
  BOOL bWaitAll,            // wait option
  DWORD dwMilliseconds      // time-out interval
);
关于函数参数:


需要注意的是第三个参数,如果为TRUE则等待的所有信号都变为有信号了才返回,否则句柄数组中只要有一个
对象变为有信号则立即返回
关于返回值:


失败和超时的两种情况与单一等待的方式相同,但在正常的返回的情况下该返回值会因为第三个参数设置的不同而
有所不同
如果第三个参数设置为TRUE则正常情况下会返回WAIT_OBJECT_0
如果第三个参数设置为FALSE则正常情况下的返回的结果为变为有信号的那个对象在句柄数组中的索引值
关于该函数还有一点需要注意的是该函数最大能够等待的句柄数量为64如果要等待更多的句柄,可以采用嵌套等待
的方式




线程的同步
线程的同步方式有多种,主要分为内核方式的同步和用户方式的同步
内核方式的同步速度要慢于用户方式的同步


用户方式的同步:
1临界区(临界区类似于电话亭,电话亭里是我们要保护的数据,因为电话亭较小一但有人进去,直到这个人离开其他人才人进去)
临界区(Critical Section)是一段被保护的代码块,在任意时
刻,最多只能有一个线程进入该代码块,即对临界区的访问是原
子的、排它的,因此常把一些需要同步的受保护资源放在临界区
中。临界区的原子性,是指在临界区中线程不能并发执行。当
然,系统仍然可以中断当前线程的执行并调度其它线程,但是系统
不会调度那些请求进入该临界区的线程,直到当前线程离开临界区。 

临界区的特点
1速度快
2容易死锁
使用临界区应注意的问题
注意在break和return语句之前离开临界区








2原子访问函数
以Interlocked开头的一些函数
内核方式的同步:
1事件方式同步(该同步有点像我们去旅店住店,服务员把房间的钥匙给我,我就可以进入房间休息,因为其他人没有钥匙其他人就不能进入该房间休息
当我休息完后把钥匙交给服务员其他人才能进入那个房间休息,即谁有钥匙谁才可以进入那个房间休息)
该方式的特点
简单灵活
需要注意的问题
注意在break和return语句之前需要设置事件为有信号
2信号量
HANDLE CreateSemaphore(
  LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // SD
  LONG lInitialCount,                          // initial count
  LONG lMaximumCount,                          // maximum count
  LPCTSTR lpName                               // object name
);



Semaphore(信号量)是一个比较重要的内核对象,创建该对象时我们需要注意第二个和第三个参数
第二个参数代表可用的信号数量
第三个参数代表可用信号数量的上限值




我们可以这样理解,一个旅店共有10个房间,那么最大可利用的房间数就是10(lMaximumCount具有类似的含义)

可用的信号数量可以这样理解旅店的老板因为有很多东西没地方放且最近生意冷淡猪店的人较少,所以他把这10个房间中的两间用来存

自己的东西
剩下的8个房间就是留给给客人住,当客人把这剩余的8个房间都住满了,那么就无可用的房间了(我们等待这个信号量将不返回),当然老板看到生意
好了把自己存放东西的房间腾出来给客人住



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值