- Tìm số 0 đầu tiên trong một mảng bit
- Unix Hiển thị thông tin về các tập tin khớp với một trong hai mẫu
- Biểu thức chính quy thay thế nhiều tệp
- Ẩn lệnh từ xtrace
我一直在阅读无锁技术,例如比较和交换以及利用 Interlocked 和 SpinWait 类来实现线程同步而无需锁定。
我已经运行了一些自己的测试,其中我只是有很多线程试图将一个字符附加到一个字符串。我尝试使用常规 khóa
和比较和交换。令人惊讶的是(至少对我而言),锁显示出比使用 CAS 更好的结果。
这是我的代码的 CAS 版本(基于 cái này )。它遵循复制->修改->交换模式:
private string _str = "";
public void Append(char value)
{
var spin = new SpinWait();
while (true)
{
var original = Interlocked.CompareExchange(ref _str, null, null);
var newString = original + value;
if (Interlocked.CompareExchange(ref _str, newString, original) == original)
phá vỡ;
spin.SpinOnce();
}
}
更简单(也更高效)的锁版本:
private object lk = new object();
public void AppendLock(char value)
{
lock (lk)
{
_str += value;
}
}
如果我尝试添加 50.000 个字符,CAS 版本需要 1.2 秒,锁定版本需要 700 毫秒(平均)。对于 100k 个字符,它们分别需要 7 秒和 3.8 秒。这是在四核 (i5 2500k) 上运行的。
我怀疑 CAS 显示这些结果的原因是因为它在最后一个“交换”步骤中失败了很多。我是正确的。当我尝试添加 50k 个字符(50k 成功交换)时,我能够计算出 70k(最好情况)和近 200k(最坏情况)失败尝试。最坏的情况是,每 5 次尝试中有 4 次失败。
所以我的问题是:
据我了解,采用 CAS 的解决方案虽然难以编码,但随着争用的增加,其扩展性和性能要比锁好得多。在我的示例中,操作非常小且频繁,这意味着高竞争和高频。那么,为什么我的测试结果并非如此?
我认为更长的操作会使情况变得更糟 -> “交换”失败率会增加更多。
PS:这是我用来运行测试的代码:
Stopwatch watch = Stopwatch.StartNew();
var cl = new Class1();
Parallel.For(0, 50000, i => cl.Append('a'));
var time = watch.Elapsed;
Debug.WriteLine(time.TotalMilliseconds);
1 Câu trả lời
问题是循环失败率和字符串不可变这一事实的结合。我使用以下参数自行进行了几次测试。
Append
10,000 次。我观察到字符串的最终长度为 80,000 (8 x 10,000),因此非常完美。对我来说,追加尝试的次数平均约为 300,000 次。所以失败率约为 73%。只有 27% 的 CPU 时间产生了有用的工作。现在因为字符串是不可变的,这意味着在堆上创建了一个新的字符串实例,并将原始内容加上一个额外的字符复制到其中。顺便说一下,这个复制操作是 O(n),所以随着字符串长度的增加,它变得越来越长。由于复制操作,我的假设是失败率会随着字符串长度的增加而增加。原因是随着复制操作花费越来越多的时间,发生冲突的可能性越来越大,因为线程花费更多时间竞争以完成 ICX。我的测试证实了这一点。您应该自己尝试同样的测试。
这里最大的问题是顺序字符串连接不适合并行处理。由于操作 XN 的结果取决于 Xn-1 ,因此采用完全锁定会更快,特别是如果这意味着您可以避免所有失败并且重试。在这种情况下,悲观策略赢得了与乐观策略的战斗。当您可以将问题划分为真正可以畅通无阻地并行运行的独立卡盘时,低技术会更好地工作。
作为旁注,使用 Interlocked.CompareExchange
来初始读取 _str
是不必要的。原因是在这种情况下读取不需要内存屏障。这是因为实际执行工作的 Interlocked.CompareExchange
调用(代码中的第二个调用)将创建一个完整的屏障。所以最坏的情况是第一次读取是“陈旧的”,ICX 操作未通过测试,并且循环旋转回来重试。然而,这一次,之前的 ICX 强制执行“全新”读取。1
以下代码是我如何使用低锁机制概括复杂操作。事实上,下面给出的代码允许您传递代表操作的委托(delegate),因此它非常通用。你想在生产中使用它吗?可能不是因为调用委托(delegate)很慢,但你至少明白了。您始终可以对操作进行硬编码。
public static class InterlockedEx
{
public static T Change(ref T destination, Func operation) where T : class
{
T original, value;
LÀM
{
original = destination;
value = operation(original);
}
while (Interlocked.CompareExchange(ref destination, value, original) != original);
return original;
}
}
1在讨论内存障碍时,我实际上不喜欢“陈旧”和“新鲜”这两个词,因为这不是它们真正的意思。与实际保证相比,它更像是一种副作用。但是,在这种情况下,它更好地说明了我的观点。
关于c# - 锁与比较和交换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19459905/
我在理解这些函数如何更新底层引用、原子等时遇到问题。 文档说:(应用当前身份值参数) (def one (atom 0)) (swap! one inc) ;; => 1 所以我想知道它是如何“扩展到
尝试让一段代码看起来更好。 我在 Clojurescript 中有以下内容: (swap! app-state assoc-in [:lastresults] []) (swap! app-state
我在数据库中有带有排序顺序号的记录。现在我想创建一个带有向上和向下按钮的用户界面来重新排序它们。制作两个 functionsUp(record) 和 functionDown(record) 的最佳算
如何才能让第二次点击时返回?我想我必须以某种方式找到活跃的,但不确定。 $("#test").click(function(){ $("#dsa").fadeOut() $("#asd
我需要有关这次考试的帮助。我需要反转输入字符串。 int main(void) { char str[30]; int strlen; int i=0; int count=0;int
我正在用 C 语言玩指针...我尝试编写一个接收指向值的指针、检索指针的指针并交换指向值的指针的交换,而不是接收指向值的指针和交换值的常规交换。 这是代码... 互换功能: void swap(voi
如何在 javascript 中切换值?例如,如果 x = apple,则函数应返回 x = orange。如果 x = orange,则函数应返回 x = apple。不确定,这里有什么用,切换或交
刚接触这类东西,可能做错了什么,但是- 我有 3 个成员 std::unique_ptr currentWeapon; std::unique_ptr weaponSlotOne; std::uniq
我想在 Map 内的不可变列表内交换项目,示例: const Map = Immutable.fromJS({ name:'lolo', ids:[3,4,5] }); 我正在尝试使用
我创建了动态数组。如果具有某些值,则填充。打印它。但是交换/交换指针后(任务是在特定条件下交换行) 条件取决于sumL。为了不浪费您的时间,我没有描述细节。 问题在于交换指针。 for ( k = 0
要反转整个 vector,存在 std::reverse。但我想将一个 vector “划分”为两部分(恰好在中间)并将两者反转,将它们放回一起并再次反转整个 vector 。例如我们有: 0 1 2
我正在致力于代码最小化和增强。我的问题是:是否可以在不破坏代码逻辑的情况下交换上面的 if 语句? int c1 = Integer.parseInt(args[0]) ; int c
我读过释放 vector 内存的最佳方法是: vector().swap(my_vector); 而且我真的不明白发生了什么。交换函数需要 2 个 vector 并交换它们的元素,例如: vector
我正在尝试编写一个 Haskell 函数,该函数接受一串字母对,并在所有字母组成的字符串中交换该对字母,但我想出的方法感觉很尴尬且不惯用。 我有 swap a b = map (\x-> if x =
我正在尝试使用向上和向下箭头交换两个元素。 JSFiddle 解决方案会很棒! 我的 HTML: Some text down Some ot
当将 subview 与另一个太阳 View 交换时,是否需要重新应用约束?是否需要删除适用于已删除 View 的约束? 或者它们应该自动持续存在? 最佳答案 约束是 View 的“一部分”。当您删除
所以我制作网站已经有一段时间了,但只是真正用于显示和信息的东西。我想尝试一下 AngularJs,所以我遵循了 Codeschool 上的指南。当我根据在线文档意识到我使用的语法不被推荐时,我在该应用
我正在尝试编写一个函数,可以将字符串中的 unicode 字符替换为非 unicode ASCII 字符,问题是上传包含它们的字符串时,unicode 连字符和引号不会被读取。 我希望该函数有一个带有
我目前正在使用 Azure 网站来部署我的应用程序。我目前正在使用两个网站,每个网站监听我的 GIT 的不同分支。如图所示here . 现在,为了让它变得完美,我只是缺少一种在这两个实例之间快速切换的
在我的 javascript 中,有两个包含一些值的 div。 我想交换这些div中的值。 有什么解决办法吗? 最佳答案 var temp = $('#div1').html(); $('#div1'
Tôi là một lập trình viên xuất sắc, rất giỏi!