您的位置:首页 > 编程语言

关于多线程编程的笔记

2008-10-24 18:47 183 查看

多线程笔记

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
创建退出:CreateThread、ExitThread、_beginthreadex、_endthreadex:ExitThread会使得线程在清理自动变量、调用C++释函前退出;CreateThread不会执行C运行时数据块的pre-thread initialization,_beginthreadex用于解决该问题;

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

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

l
线程池:QueueUserWorkItem;

l
优先级:SetProcessPriorityBoost、SetThreadPriorityBoost;

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

l
纤程fiber:提供用户级的线程支持,ConvertThreadToFiber将当前线程转化为主纤程,SwitchToFiber、DeleteFiber、GetCurrentFiber;

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表示当前线程;

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

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

四、 GDB线程调试

l
创建通告,[New Thread xxx]

l
所有线程信息,info threads

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

l
切换线程,thread threadnum[/i],threadnum[/i]由info threads返回;

l
向一组线程发消息:thread apply all[/i] bt,向所有线程发bt消息;thread apply 1 2 bt,向线程1、2发消息;

五、 常见问题

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问题:x从A被改成B,后又改成A;

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

3) Free操作是一个复杂的问题;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: