spinlock进阶考虑

自旋锁在前面的博文中已经介绍过(Interlocked函数族),用自旋锁的缺点就是不停的询问CPU某个事件是不是发生了,某个变量是不是更改了值什么的,浪费CPU时间。因此推荐短期能够访问到的资源使用自旋锁,不然就用内核对象进行互斥或等待。

除此之外,读书过程中那两个作者基佬还提到了说,如果访问同一自旋锁的线程优先级各不相同的话,那么自旋锁就会更降低效率。

如下代码:

这是主线程中的等待代码

HANDLE hThread = (HANDLE)_beginthreadex(NULL,NULL,Thread_Low,NULL,CREATE_SUSPENDED,NULL);
SetThreadPriority(hThread,THREAD_PRIORITY_IDLE);
SetThreadAffinityMask(hThread,0x00000001);
SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_HIGHEST);
SetThreadAffinityMask(GetCurrentThread(),0x00000001);
ResumeThread(hThread);
CloseHandle(hThread);
while(InterlockedExchange(&g_x,FALSE) == FALSE);

这是线程函数

unsigned int _stdcall Thread_Low(void* pParam)
{
	InterlockedExchange(&g_x,TRUE);
	return 0;
}


从代码中可以看到,主线程的优先级要比第二线程的优先级高很多,那么在这种情况主线程等待自旋锁将造成严重的时间延迟。

从线程优先级和调度的概念上我们可以知道,由于主线程一直都处于可调度状态,而且优先级高,它就一直都在占用CPU,第二线程只有快被饿死的时候才会被OS动态提升线程优先级从而得以运行。于是,对全局变量g_x的改变被延后了数秒才得以进行。从而,主线程需要延迟数秒才能从spinlock中退出。

以下是完整代码,将两个线程的CPU都设置成只允许在1号CPU上运行是为了模拟在单核状态下CPU时间如何被高优先级的线程一直侵占。

volatile LONG g_x = FALSE;
unsigned int _stdcall Thread_Low(void* pParam)
{
	InterlockedExchange(&g_x,TRUE);
	return 0;
}
void main()
{
		 HANDLE hThread = (HANDLE)_beginthreadex(NULL,NULL,Thread_Low,NULL,CREATE_SUSPENDED,NULL);
		 _ASSERT(SetThreadPriority(hThread,THREAD_PRIORITY_IDLE));
		 SetThreadAffinityMask(hThread,0x00000001);
		 _ASSERT(SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_HIGHEST));
		 SetThreadAffinityMask(GetCurrentThread(),0x00000001);
		 ResumeThread(hThread);
		 CloseHandle(hThread);
		 LARGE_INTEGER liFreq = {0};
		 LARGE_INTEGER liStart = {0};
		 LARGE_INTEGER liEnd = {0};
		 QueryPerformanceFrequency(&liFreq);
		 QueryPerformanceCounter(&liStart);
		 while(InterlockedExchange(&g_x,FALSE) == FALSE);
		 QueryPerformanceCounter(&liEnd);
		 cout<<(liEnd.QuadPart - liStart.QuadPart) / (double)liFreq.QuadPart<<'s'<<endl;
		 return;
}

如果删除对CPU亲缘性的要求,那么执行效率会快很多,这是因为低级线程被分配到其它CPU上去了。

总结:如果你用自旋锁,那么记得在等待时调用SwitchToThread以使得低等级的线程可以运行。否则你将浪费大量的CPU时间并将导致低等级的线程不必要的长延迟

实践:在main的while循环中加上SwitchToThread看看和不加,延迟时间各是多少。

另外我有一个问题,Sleep(0)按照书中的说法,不是只调度同级或者高级线程么,为什么我用Sleep(0)来放弃时间片得到的效果和SwitchToThread差不多呢?按照我的理解来说,Sleep(0)应该改善不了多少状况啊,求大大解答。


自问自答一下:

Sleep(0)除了XP和2000调用同级或者高级线程外,其余都是调用所有线程

OSEK(Open Systems and the Automotive Electronics Architecture)标准中,Spinlock是一种用于保护共享资源的同步机制。Spinlock是一种简单而轻量级的锁机制,用于实现互斥访问共享资源的目的。 Spinlock的原理是通过忙等待的方式来实现互斥。当一个任务需要访问共享资源时,它会尝试获取该资源的Spinlock。如果Spinlock已经被其他任务获取,则该任务会一直在循环中等待,直到Spinlock被释放。一旦Spinlock被释放,等待的任务会立即获取Spinlock,并开始执行对共享资源的操作。其他任务在获取Spinlock之前会一直等待。 使用Spinlock需要注意以下几点: 1. 忙等待:由于Spinlock是通过忙等待的方式实现互斥,所以当等待的任务较多时,会浪费CPU资源。因此,在设计中需要合理选择使用Spinlock的场景和资源。 2. 优先级反转:如果一个高优先级任务在等待低优先级任务释放Spinlock时发生阻塞,这种情况被称为优先级反转。为了解决这个问题,可以使用优先级继承或优先级屏蔽等技术。 3. 死锁:如果多个任务同时尝试获取多个Spinlock,并且顺序不一致,可能发生死锁。因此,在使用多个Spinlock时,需要遵循一定的获取顺序,以避免死锁情况的发生。 SpinlockOSEK标准中提供的一种简单而有效的同步机制,适用于对共享资源进行短暂访问的场景。在实际使用中,需要根据具体系统的需求和性能要求,谨慎使用Spinlock,并考虑其带来的开销和潜在问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值