Spin Lock -- TAS和TTAS

本文通过对比TAS与TTAS两种锁机制的性能,揭示了Test&Set操作带来的总线广播问题及其对锁性能的影响。并介绍了如何通过采用指数退避策略来优化锁性能。

TAS采用原子操作更新共享状态,同时添加while循环,保证在无法获得锁的同时,可以重复尝试获取锁(实现自旋),而不是挂起线程。如果使用java的话,则可以使用compareAndSet原子操作。

以下是java的TAS版本:

import java.lang.reflect.Field;
import sun.misc.Unsafe;

public class TAS {
	
	private int state = 0;

	public static long offset = 0;

	public static Unsafe unsafe = null;
	public void lock(){
		while(!unsafe.compareAndSwapInt(this, offset, 0, 1));
	}
	
	public void unlock(){
		unsafe.compareAndSwapInt(this, offset, 1, 0);
	}
	static {
		unsafe = getUnsafe();
		try {
			offset = unsafe.objectFieldOffset(TAS.class.getDeclaredField("state"));

		} catch (NoSuchFieldException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private static Unsafe getUnsafe() {
		try {
			Field field = Unsafe.class.getDeclaredField("theUnsafe");
			field.setAccessible(true);
			return (Unsafe) field.get(null);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 

		return null;
	}
}

测试程序:

public class Test {
	private int a = 0;
	
	public TAS tas = new TAS();
	public void increment(){
		tas.lock();
		a++;
		tas.unlock();
		System.out.println(a);
	}
	
	public static void main(String[] args) {
		final Test test = new Test();
		for(int i = 0; i < 10000; i++){
			new Thread(new Runnable() {
				@Override
				public void run() {
					test.increment();
				}
			}).start();;
		}
	}
}

根据多次运行的结果可以看到,锁正常运行。

TTAS的锁实现和TAS的实现相类似。来看一看TAS和TTAS的性能对比,


总的来看,TAS的性能非常糟糕,虽然TTAS相比于TAS性能要好一些,但是和理想值的对比还是差了很多。同时两个曲线都比较陡,也就是说随着线程数的增加,锁的性能越来越差。那么什么导致了锁的性能这么差呢?

上面的图给出了理由:

Test&Set()导致了总线上频繁的广播,用于更新各个线程的内存缓存。因此当持有锁的那个线程释放锁的时候,由于总线的繁忙,而导致了延迟。

为了解决这个问题,可以采用Exponential Backoff


所谓backoff就是当线程无法获取锁的时候,进行休眠一定的时间。这个就在无限休眠和自旋等待之间获得了一个平衡。

来看看改进之前和改进之后的性能差别,


可以看到改进之后,明显地改善了性能。当然这么做也是优缺点的,也就是你必须小心的选择休眠的时间。否则会得不偿失。


总体性能并不是TAS和TTAS最大的问题,其最大的问题是可能会导致starve(饥渴)的出现。由于采用while(自旋锁)实现,所以当某个线程释放锁的时候,其他锁获取这个锁的概率是相同的,但是在最坏情况下,某个线程很早就请求锁,但是每次其他线程释放锁它都无法获取到锁,这就使得这个线程的执行时间非常长,导致了饥渴的出现。

使用基于排队的自旋锁就可以避免线程的饥渴。接下来的文章会介绍。

在 Robot Framework 中,如果你需要**先定位到某个特定 class 的元素**(如 `class="ant-spin ant-spin-lg ant-spin ant-spin-spinning css-18uajt0"`),然后再判断它的 `aria-busy` 是否为 `"true"`,你可以使用 **XPath 或 CSS Selector** 来定位该元素。 --- ### ✅ 完整示例代码 ```robotframework *** Settings *** Library SeleniumLibrary *** Test Cases *** Check ARIA Busy on Spin Element Open Browser https://your-app-url.com Chrome Maximize Browser Window # 等待目标元素出现(使用 XPath 匹配 class) Wait Until Element Is Visible xpath=//div[contains(@class, 'ant-spin-spinning') and contains(@class, 'ant-spin-lg')] 15s # 获取 aria-busy 属性值 ${aria_busy} = Get Element Attribute xpath=//div[contains(@class, 'ant-spin-spinning') and contains(@class, 'ant-spin-lg')]@aria-busy # 判断是否为 "true" Should Be Equal As Strings ${aria_busy} true ignore_case=True Close Browser ``` --- ### 📌 解释说明: #### 🔎 定位方式:XPath + class 匹配 ```xpath //div[contains(@class, 'ant-spin-spinning') and contains(@class, 'ant-spin-lg')] ``` 由于 `class` 是多个类名的组合,并且顺序可能变化,使用 `contains()` 是更灵活的方式。上面的表达式表示: - 查找一个 `<div>` 元素; - 它的 `class` 属性中包含 `'ant-spin-spinning'`; - 并且也包含 `'ant-spin-lg'`。 你也可以根据实际 HTML 结构进一步精确定位,比如加上 `@aria-busy` 或其他属性过滤。 --- ### 🧩 可选:使用 CSS Selector 定位 ```robotframework Wait Until Element Is Visible css=div.ant-spin.ant-spin-spinning.ant-spin-lg ${aria_busy} = Get Element Attribute css=div.ant-spin.ant-spin-spinning.ant-spin-lg@aria-busy ``` CSS Selector 更简洁,但只适用于你知道确切 class 名称的情况。 --- ### 💡 提示与最佳实践: - 如果这个 `Spin` 组件是动态加载的(例如由 Ajax 触发),一定要使用 `Wait Until Element Is Visible` 或 `Wait Until Element Is Enabled` 这样的显式等待。 - 如果页面中有多个 `.ant-spin` 元素,建议结合父级结构或 `id`、`data-*` 属性进一步限定。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值