第二章,一些基本的w32线程操作

线程的行为。
核心对象的概念,线程和线程对象的概念。
建立一个线程,释放线程,监听线程,结束线程的win32方法。

一、    API: CreateThread,创建一个线程
HANDLE CreateThread(
  LPSECURITY_ATTRIBUTES lpThreadAttributes,  // pointer to security attributes
  DWORD dwStackSize,                         // initial thread stack size
  LPTHREAD_START_ROUTINE lpStartAddress,     // pointer to thread function
  LPVOID lpParameter,                        // argument for new thread
  DWORD dwCreationFlags,                     // creation flags
  LPDWORD lpThreadId                         // pointer to receive thread ID
);

1.    一些说明
1)    返回值可以用来判断创建是否成功,成功时返回值是Thread的handle,可以用来指向该线程,从后面看,获取线程返回值需要该handle。
2)    一般调用方式( NULL,0,函数名,(LPVOID)函数参数,0,&ThreadId )
a)    Security属性默认,stackSize为0则大小默认为一兆;
b)    启动方式0为“立即开始执行”,我认为是与“挂起”对立的概念,后面的执行中看到,也并非能保证线程真的立即执行。
c)    大部分API函数用handle, ThreadId有时候有用,比如两个影响其他线程消息的函数。
3)    线程函数
a)    名字任意
b)    返回值是DWORD,调用约定是WINAPI,参数是LPVOID
c)    该参数可以是一个void型指针,也可以直接当Int用。




二、    多线程的行为
1.    执行具体过程受多方面因素影响,无法预期,也无法重现。【所以某次的失败,之后也无法重现他是怎么失败的】
2.    线程之间的执行次序应该认为是随机的,并不是先启动的就先执行。
3.    task switches可能在任何时间发生
1)    书上例子没有用 C runtime library,于是出线了printf执行到一半被打断的情形
2)    用vc6调试的结果,没有出线打断情况,但launch输出行和数字行都有被打多的情况。【具体原因不明,大概是由C runtime library引起】
4.    线程的实际执行情况,对程序上的小改变,高度敏感。
5.    虽然dwCreationFlag是0,但是线程并不是立即启动的【稍后讨论?】


三、    API:CloseHandle与核心对象
BOOL CloseHandle(
  HANDLE hObject   // handle to object to close
);

1.    核心对象的概念
1)    和GDI对象一样,不需要知道其数据结构,只需要得到其handle,通过调用API函数使用
2)    与GDI对象不同,只有一种handle——HANDLE;且可以有多个拥有者。
3)    包括process, thread, files, events, semaphores, mutexes, pipes
2.    最出乎意料的概念:
线程与线程对象( Thread and Thread object )不是一个概念,也不是一个东东。
线程对象与该对应的线程有一种引用关系,再加上调用CreateThread并拥有该线程对象的线程,一个线程对象初始默认的计数器为2.
拥有线程CloseHandle会导致计数器-1,所引用线程结束再导致计数器-1,该线程计数器才会降为0.
3.    (结合第三章)关于线程的结束和激活:
线程结束,return或ExitThread,线程不在了,但线程对象还在,其线程索引计数器-1,但还不为零。线程结束导致线程对象成为激活状态,在wait该线程对象的线程被唤醒
4.    reference count和清扫(clean up)
1)    每个核心对象可以有多个拥有者,所以要计数。【并记录都有谁拥有他】
2)    线程或进程结束时会clean up,其动作包括对所拥有对象计数器-1.
3)    根据核心对象的性质不同,对象的拥有者可以是进程或线程,则clean up的时机不同。
5.    虽然clean up最后也会清扫,但很【有必要手动close handle】
1)    核心对象由多个进程拥有时,释放顺序可能是重要的【想象不出…】
2)    某线程可能拥有很多对象,不释放是一种内存泄露。
3)    由于某些对象由进程拥有,线程不释放的话,等clean up释放会很滞后,产生内存泄露问题。
6.    引用计数机制产生的奇妙情况
1)    一个核心对象可被多个线程或进程拥有,所以可能出线创造者作古,而核心对象继续为其他进程或线程使用的情况。
2)    可能会有一个新的线程来调用CloseHandle(), 来取代之前的线程。【未完全明白。】


四、    API:GetExitCodeThread() 获得线程函数的返回值、也可用来监听线程(busy wait)
BOOL GetExitCodeThread(
  HANDLE hThread,      // handle to the thread
  LPDWORD lpExitCode   // address to receive termination status
);

1.    返回值不表示线程结束,只表示函数调用成功。
2.    两个参数,一个参数为handle,一个表示返回值或STILL_ACTIVE
3.    就是说handle给主线程一个机会获得新线程的返回值。
4.    可以反复调用该函数测试分线程是否结束,但会形成busy wait。


五、    API:ExitThread 终结一个线程
VOID ExitThread(
  DWORD dwExitCode   // exit code for this thread
);

1.    该函数强制终结本线程,而不是终结其他线程。
2.    如果某函数调用了这个API函数,则那个函数不会返回,那个函数调用之后的代码也不会被执行。
3.    如果某线程中出线了上述情况,该线程的返回不会被执行到,GetExitCodeThread得到的返回值由ExitThread提供
4.    不建议在主线程中使用该函数。

六、    主线程、结束主线程
1.    两个特点:
1)    必须负责GUI程序中的主消息循环
2)    主线程中使用ExitThread,其他work线程依然存在,可以编程检验。参见MSDN: ExitThread。
3)    主线程中使用ExitThread在书中也强调会强制终止分线程,使分线程没有机会clean up,从而造成工作未完成和泄露。【我猜测,可能MFC创建的GUI程序会有这种机制】
2.    很多不确定的,确定的如下:
1)    尽量不要在主线程中使用ExitThread。
2)    主线程结束前应等待其他线程结束。【见第三章:在消息循环中加入线程计数判断】

七、    MTVERIFY适用于gui和console程序

八、    后台打印线程实例:一些注意点,和铺垫
1.    win32中线程分为GUI线程和worker线程。Worker线程不应该创建窗口,因为创建了就需要管理消息循环。Worker线程的任何输出,甚至报错,都应该授权GUI线程来做。
2.    简单、安全更重要于复杂、速度;表面积尽量少——准备好数据,打包之后再开始线程。
3.    全局变量容易被改写,局部变量容易过生存期,heap最安全。主线程中申请,分线程中释放。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值