它并不像看起来那么简单,除了两种情况:
- 当只有读者而没有作者时,你永远不需要同步。
- 当有多个线程写入并且其中至少一个正在执行读取-修改-写入操作(例如
++x;
)时,您总是需要同步,否则您将得到完全不可预测的结果。
在所有其他情况下,如果您至少有一个写入器和一个读取器(或多个写入器),您通常(除了极少数例外)需要同步访问,但不一定总是,也不总是以最严格的方式。
这在很大程度上取决于您需要什么保证。一些应用程序需要严格的线程顺序一致性(有时您甚至需要锁公平性)。如果某些应用程序在同一个线程中仅具有发生前发生的保证,它们将同样运行良好,但性能要好得多。然而,其他应用程序甚至不需要那么多,并且对轻松的操作或根本没有任何保证完全满意。
例如,工作线程的这个“典型”实现有一个 writer 和一个 reader:
// worker thread
running = true;
while(running) { task = pull_task(); execute(task); }
// main thread exit code
running = false;
join(workerthread);
这在没有任何同步的情况下工作得很好。是的,从迂腐的角度来说,它的值何时或如何改变是不确定的running
,但实际上它没有区别。内存位置不可能有一些“随机”的中间值,并且更改是否在几十纳秒之前或之后变得可见并不重要,因为工作线程很可能无论如何都忙于执行任务,并且在最坏的情况下,它会在几毫秒后获取更改。最终,在下一次迭代中,工作线程将接受更改并退出。
几年前在 Dobb 博士发表的 SPSC 快进队列采用了类似的原理,只是使用了指针。
GCC 文档中对许多不同的同步模式及其含义进行了全面而全面的阅读。