windows程序设计学习笔记--多任务和多线程

来源:互联网 发布:非诚勿扰程序员死亡 编辑:程序博客网 时间:2024/06/10 06:46

windows中的数据传输:剪切板,动态数据交换,对象链接和嵌入。

非抢占式多任务,指 处理完一个消息才处理下一个消息。
帮助解决非抢占式多任务局限方式:
1.设置鼠标图标状态。
2.Windows计时器。
3.PeekMessage调用。
GetMessage必须从消息队列中取到消息才返回。PeekMessage无论是否取到消息都返回,在长时间运行任务中掺入,可对新消息做及时反应。

串行化消息队列:
在一个上一个用户输入消息被完全处理前,不会把下一个键盘或鼠标消息发送给任何程序。

非串行消息队列:
操作系统在线程中做抢占式的控制切换。

OS/2 PM中的多任务:
每个线程可创建或不创建消息队列。
要在现场内创建窗口,则须创建消息队列。

没消息队列的线程不处理消息,不能向有消息队列的窗口发消息,可以给消息队列所属的线程发消息。

这样OS/2 PM下,一个线程来生成窗口和处理窗口消息,和一个或多个非消息队列线程来处理需要长时间处理的任务。可以满足多任务。

一个特定程序中,所有的线程都是同一个进程的一部分。因此,共享进程的资源。然而,每个线程有自己的堆栈,所以每个线程的自动变量是唯一的。每个线程也有它自己的处理器状态,这些在线程切换时会被自动保存和恢复。

抢占式多任务系统能在任一点中断一个线程,把控制切换到另一个线程。
容易造成多线程下的混乱,管理这种混乱而引入的机制有,信号灯,临界区。
信号灯:在代码的某处,暂停一个线程。直到另一个线程发信号让他继续。
临界区:不能被中断的代码段。

windows对多任务的好处:
1.32位的Windows有一个非串行的消息队列。
体现:一个程序处理大任务时,可以把焦点切到其他程序。其他程序仍然正常工作。
2.Windows中,每个线程在被生成时都有自己的消息队列。
3.有一个函数能让一个线程终止进程中的另一个线程。
4.线程本地存储。就是让每个线程可以有只属于自己的静态变量。

C运行库的:
// 开始一个线程
hThread = _beginthread(ThreadProc, uiStackSize, pParam);

// 线程函数
void __cdecl ThreadProc(void * pParam);

// 结束线程
_endthread () ;

Sleep(int); // 挂起所在线程,参数为0,使线程现有的时间片作废。参数单位毫秒。

多线程同步手段:临界区

适用于:避免多线程可能会造成的数据冲突。
临界区:一段不会被中断的代码。

// 应该定义为全局变量,让进程内各个线程都可使用
CRITICAL_SECTION cs;

// 初始化。临界区对象不能被移动,复制和修改。由系统维护。
InitializeCriticalSection(&cs);

// 临界区对象被初始化后,用以下函数进入临界区
// 进入后,就可以说,这个线程用于这个临界区对象
// 两个线程不能同时拥有一个临界区。若一个线程进入了临界区,则下一个线程在调用此函数进入同一临界区对象时,会被挂起。
// 直到第一个线程调用LeaveCriticalSection离开该临界区时,才返回
EnterCriticalSection(&cs);

// 离开临界区
LeaveCriticalSection(&cs);

// 当程序不再需要临界区对象时,用以下函数删除它
DeleteCriticalSection(&cs);

// 一般一个进程中有多个线程,对多线程间共享的对象操作时,需要用到临界区对象。
// 有多个共享对象时,一般一个共享对象对应用一个临界区对象
// 临界区对象只能在一个进程内使用

多线程杂谈:
多线程中定义变量时,可能要加关键字volatile,来告诉编译器此变量可能会在外部被改变,以阻止编译器对此变量使用的一些地方做优化。如 条件判断地方,不执行等。

线程内申请分配的资源,在线程退出时,不会自动销毁,因为资源属于进程。一般退出时,显示销毁线程中申请的资源。

多线程同步:事件对象

事件对象可以用来避免反复的创建和删除线程。
一个事件对象有两种状态:触发和非触发。

// 创建事件对象
hEvent = CreateEvent(&sa, fManual, fInitial, pszName);
参数1,4只在事件对象在进程间共享时,才有意义。在单进程程序中,通常设为NULL。
fInitial:事件的初始触发状态。TRUE,初始时,已触发。FALSE,初始时,非触发。
fManual:为FALSE时, 在函数WaitForSingleObject返回后,事件对象会被自动设置为未触发。TRUE,不会。

// 触发一个已有的事件对象
SetEvent(hEvent);

// 解除一个事件对象的触发状态
ResetEvent(hEvent);

// 等待一个事件对象被触发
// 如果事件对象已经触发,立刻返回。否则,等待dwTimeOut毫秒。dwTimeOut为INFINITE,一直等待。
WaitForSingleObject(hEvent, dwTimeOut);

多线程相关支持:线程本地存储

微软扩充了C语言以加入此机制。对线程本地存储对象前加上 __declspec (thread)

1.

// 分配一个线程本地存储索引.// 此进程的任何线程后续可以使用这个索引来存储和获取值// 这些值是线程本地的,因为每个线程接收自己槽对这个索引DWORD WINAPI TlsAlloc(void);

返回值:
成功,一个本地线程存储索引。索引的槽初始化为0.
失败,TLS_OUT_OF_INDEXES

额外:
线程本地存储索引被分配后,每个进程内的线程可以用它来获取自己的线程本地存储 存储槽。要存储一个值在线程本地存储槽,线程需调用TlsSetValue指定索引。线程在后续TlsGetValue中指定相同索引来取得存储的值。
线程本地索引,只在一个进程内有效。

2.

// 存储值在调用线程的线程本地存储槽对指定的线程本地存储索引// 每个进程里的线程有自己的槽对每个线程本地存储索引BOOL WINAPI TlsSetValue(  _In_     DWORD  dwTlsIndex,  _In_opt_ LPVOID lpTlsValue);

参数解释:
dwTlsIndex:线程本地存储索引,被TlsAlloc函数分配。
lpTlsValue:被存储在调用线程的对应于指定索引的线程本地存储槽的值。
返回值:
成功,非0
失败,0

3.

// 取得值,值在调用线程的针对此线程本地索引的线程本地存储槽里LPVOID WINAPI TlsGetValue(  _In_ DWORD dwTlsIndex);

返回值:
成功,值
失败,0.此时也可能存储的值就是0,须结合GetLastError判断是否是失败了

4.

// 释放线程本地存储索引LPVOID WINAPI TlsGetValue(  _In_ DWORD dwTlsIndex);

如果线程内进程已经分配了内存和存储了一个指向分配内存的指针到线程本地存储槽,他们应该释放内存,在调用TlsFree前。TlsFree不会释放地址存储在本线程本地索引的线程本地存储槽内地址指向的内存

0 0