Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

关于 logic 的一个遗留问题 #163

Closed
Timothy-Liuxf opened this issue Mar 23, 2023 · 2 comments
Closed

关于 logic 的一个遗留问题 #163

Timothy-Liuxf opened this issue Mar 23, 2023 · 2 comments
Assignees
Labels
bug Something isn't working good first issue Good for newcomers

Comments

@Timothy-Liuxf
Copy link
Member

Describe the bug
在:#162 (comment) 中描述了这点,是我自己以前的锅,之后看看如何改吧。

Expected behavior
消除 data race

Additional context
Add any other context about the problem here.

@Timothy-Liuxf Timothy-Liuxf added the bug Something isn't working label Mar 23, 2023
@Timothy-Liuxf Timothy-Liuxf added the good first issue Good for newcomers label Mar 23, 2023
@Timothy-Liuxf
Copy link
Member Author

Timothy-Liuxf commented Apr 26, 2023

刚在评论里加了点东西,之后为了方便直接贴在这了:

这里应该是一直遗留下来的错误,属于是我当初犯下的错误之一(其实也是照着 THUAI3.0 抄的,但我把这个错误发扬光大了 x)。写操作加锁读操作却不加锁的情况下,这个锁是无意义的。主要有以下三种解决方案:

  1. 读写都加锁,即读的时候也锁起来(但是开销较大)

  2. 读写全不加锁,在外面加(不推荐,会很麻烦)

  3. 使用原子操作(最推荐,但原子操作坑较多,不好把握,而且由于当前这种是凑合能连续两年跑起来的,改了之后说不定会出现什么连锁 bug):

private int canOpen = 0;
private bool CanOpen
{
    get => Interlocked.CompareExchange(ref canOpen, 0, 0) != 0;
    set => Interlocked.Exchange(ref canOpen, value ? 1 : 0);
}

有几个问题需要注意:

  • bool 类型没有提供 Interlocked 操作,需要的话可以像上面的示例一样,用 int 做字段,通过其是否等于 0 对外提供 bool 接口
  • int 没有直接读写的 ReadWrite 操作,可以像上面一样使用 CompareExchangeExchange 来实现读写

有趣的是,微软声称对小于计算机字长的类型(例如 int)的直接操作都是原子的,所以不需要 Interlocked 操作,但是,例如 在这个回答当中,把里面的 double 都改成 int,在 update_variable 执行后 Sleep 足够长时间再打印 myVar2,使用 .NET 6 Release 编译运行,结果发现 myVar2 的值始终是 0!说明编译器优化过程中直接优化掉了对 myVar2 的反复赋值,或优化掉对其的读取等等,总之没有考虑原子性。说明微软文档所作的保证,不可靠!!!——不要相信微软的鬼话!!!(但更神奇的是,在 .NET 7 Release 上运行居然是结果正确的???)

刚发现一个更简单的例子:

int i = 0; new Thread(() => { Thread.Sleep(1000); i = 1; }) { IsBackground = true }.Start(); while (i == 0) ;

上述代码在 .NET 6 Release 会导致死循环(.NET 7 Release 竟然不会死循环),这说明对 i 的操作至少在 .NET 6 上并不是原子的!

@shangfengh
Copy link
Member

由于不少变量都是相关联的,最后我是决定采用多数直接读写均加锁,部分读写锁,少量原子操作的方式

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working good first issue Good for newcomers
Projects
None yet
Development

No branches or pull requests

2 participants