现在的位置: 首页 > 综合 > 正文

关于多线程编程的笔记

2013年05月12日 ⁄ 综合 ⁄ 共 2962字 ⁄ 字号 评论关闭

多线程笔记

2008-8-9   version 1.0

 

一、  超线程Hyper-Threading

Intel实现的Simultaneous
Multi-Threading
技术:一个物理核模拟多个逻辑核;

原理:定义线程只需要体系结构状态信息即可,包括寄存器、cache,通过复制这些状态信息创建多个逻辑处理器,而执行资源(计算资源?执行资源的进一步细分?)被这些逻辑处理器共享;OS可以将多个线程调度到CPU上,但执行资源只有一个,所以微体系结构需要确定线程切换。

也就是说,超线程只是在CPU上保存了两个线程的状态信息,微体系自己确定其调度,以供OS同时调度两个线程到CPU上执行?

HT通过减少CPU闲置时间来提高系统吞吐量,一般约30%

 

加速比:可加速部分,程序多核加速能力受限于不可加速部分的比例;

二、  多线程设计的分解方式

分解:进行任务划分,并确定任务间的依赖关系;

l        
任务分解:不同程序行为使用不同的线程,如GUI的用户界面线程与工作线程;

l        
数据分解:多个线程对不同的数据块执行相同的操作,如用于音视频处理;

l        
数据流分解:一个线程的输出作为另一个线程的输入;

n        
必须避免等待生产者线程结果的过程中,出现消费者线程必须闲置的情况;

n        
生产者与消费者之间的交互调度方案;

n        
所有线程之间的负载平衡;

可扩展性:性能能否随CPU数量线性增长;

 

栅栏:用于多核、多处理系统环境中保证存储操作的一致性;

栅障:使线程在控制流的某个逻辑点上集合;

 

三、  WinAPI

l        
创建退出:CreateThreadExitThread_beginthreadex_endthreadexExitThread会使得线程在清理自动变量、调用C++释函前退出;CreateThread不会执行C运行时数据块的pre-thread initialization_beginthreadex用于解决该问题;

l        
暂停恢复:SuspendThreadResumeThreadTerminateThread,挂起操作可能是危险的,如线程不会释放已经占用的锁,而TerminateThread会使得已经占用的锁无法再释放;

l        
原子操作:InterlockedIncrement等一系列;InitializeSListHeadInterlockedPushEntrySlist等对链表的原子操作;

l        
线程池:QueueUserWorkItem

l        
优先级:SetProcessPriorityBoostSetThreadPriorityBoost

l        
处理器亲和(Affinity):SetThreadAffinityMaskSetProcessAffinityMask,强制处理器绑定,SetThreadIdealProcessor建议亲和;通过GetSystemInfo获得CPU信息;

l        
纤程fiber:提供用户级的线程支持,ConvertThreadToFiber将当前线程转化为主纤程,SwitchToFiberDeleteFiberGetCurrentFiber

l        
线程命名:使用自定义的函数SetThreadName,仅用于调试器或Windbg;在MSDN上找到的函数实现如下:

//

// Usage: SetThreadName (-1, "MainThread");

//

#define MS_VC_EXCEPTION 0x406D1388

 

typedef struct tagTHREADNAME_INFO

{

   DWORD dwType; //
Must be 0x1000.

   LPCSTR szName;
// Pointer to name (in user addr space).

   DWORD
dwThreadID; // Thread ID (-1=caller thread).

   DWORD dwFlags;
// Reserved for future use, must be zero.

} THREADNAME_INFO;

 

void SetThreadName( DWORD dwThreadID, LPCSTR
szThreadName)

{

   THREADNAME_INFO
info;

   info.dwType =
0x1000;

   info.szName =
szThreadName;

   info.dwThreadID
= dwThreadID;

   info.dwFlags =
0;

 

   __try

   {

     
RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(DWORD),
(DWORD*)&info );

   }

  
__except(EXCEPTION_CONTINUE_EXECUTION)

   {

   }

}

dwThreadID设置为-1表示当前线程;

 

VS2005tracepoint——执行到该点时执行某些操作,如调用vs宏;

 

Windows中,不能在单个进程空间中混用VC提供的静态库和动态库,特别注意exe与用到的dll应使用相同的C库;

 

四、  GDB线程调试

l        
创建通告,[New Thread xxx]

l        
所有线程信息,info threads

l        
特定线程上的断点,break linespec thread threadnum [if a > 3]

l        
切换线程,thread threadnumthreadnuminfo threads返回;

l        
向一组线程发消息:thread apply all bt,向所有线程发bt消息;thread apply 1 2 bt,向线程12发消息;

五、  常见问题

1,  检查程序性能:

OS空闲循环的原因:负载不均衡、被阻塞的同步、过多的串行区、cache未中次数过多、伪共享;

2,  线程过多

控制线程数为核心数或外层cache数,如HT只有一个cache,这样在线程需要较多cache而互相争夺cache时,应将线程数限制为cache数;

将计算线程与I/O线程分离,计算线程大多数时间是可运行线程,I/O线程多数时间在等待外部事件;

3,  死锁的处理

a)        
使用数据复制避免使用锁;

b)       
锁获取顺序,在获得B前必须先获得A

c)       
尝试获取,获得A后尝试获得B,如果失败则释放A

4,  锁竞争激烈

a)        
不使用锁

b)       
细粒度锁

c)       
读写锁

5,  非阻塞算法,基于原子操作

一个有趣的算法

long x_old, x_new, x_was;

do{

        x_old = x;

x_new = fun(x_old);

x_was = interlockedcompareexchange(&x, x_new,
x_old);

       }while(x_was!=x_old)

1)  该算法可能导致ABA问题:xA被改成B,后又改成A

2)  非阻塞算法可能导致cache行乒乓现象

3)  Free操作是一个复杂的问题;

 

抱歉!评论已关闭.