June 23, 2009
Posted by Bartosz Milewski under C++, Concurrency, D Programming Language, Java, Multithreading, Programming, Type System
1 Comment
If it weren’t for the multitude of opportunities to shoot yourself in the foot, multithreaded programming would be easy. I’m going to discuss some of these “opportunities” in relation to global variables. I’ll talk about general issues and discuss the ways compilers can detect them. In particular, I’ll show the protections provided by my proposed extensions to the type system.
Global Variables
There are so many ways the sharing of global variables between threads can go wrong, it’s scary.
Let me start with the simplest example: the declaration of a global object of class Foo (in an unspecified language with Java-like syntax).
Foo TheFoo = new Foo;
In C++ or Java, TheFoo would immediately be visible to all threads, even if Foo provided no synchronization whatsoever (strictly speaking Java doesn’t have global variables, but static data members play the same role).
If the programmer doesn’t do anything to protect shared data, the default immediately exposes her to data races.
The D programming language (version 2.0, also known as D2) makes a better choice–global variables are, by default, thread local. That takes away the danger of accidental sharing. If the programmer wants to share a global variable, she has to declare it as such:
shared Foo TheFoo = new shared Foo;
It’s still up to the designer of the class Foo to provide appropriate synchronization.
Currently, the only multithreaded guarantee for shared objects in D2 is the absence of low-level data races on multiprocessors–and even that, only in the safe subset of D. What are low level data races? Those are the races that break some lock-free algorithms, like the infamous Double-Checked Locking Pattern. If I were to explain this to a Java programmer, I’d say that all data members in a shared object are volatile. This property propagates transitively to all objects the current object has access to.
Still, the following implementation of a shared object in D would most likely be incorrect even with the absence of low-level data races:
class Foo {
private int[] _arr;
public void append(int i) {
_arr ~= i; // array append
}
}
auto TheFoo = new shared Foo;
The problem is that an array in D has two fields: the length and the pointer to a buffer. In shared Foo, each of them would be updated atomically, but the duo would not. So two threads calling TheFoo.append could interleave their updates in an unpredictable way, possibly leading to loss of data.
My race-free type system goes further–it eliminates all data races, both low- and high-level. The same code would work differently in my scheme. When an object is declared shared, all its methods are automatically synchronized. TheFoo.append would take Foo’s lock and make the whole append operation atomic. (For the advanced programmer who wants to implement lock-free algorithms my scheme offers a special lockfree qualifier, which I’ll describe shortly.)
Now suppose that you were cautious enough to design your Java/D2 class Foo to be thread safe:
class Foo {
private int [] _arr;
public synchronized void append(int i) {
_arr ~= i; // array append
}
}
Does it mean your global variable, TheFoo, is safe to use? Not in Java. Consider this:
static Foo TheFoo; // static = global
// Thread 1
TheFoo = new Foo();
// Thread 2
while (TheFoo == null)
continue;
TheFoo.append(1);
You won’t even know what hit you when your program fails. I will direct the reader to one of my older posts that explains the problems of publication safety on a multiprocessor machine. The bottom line is that, in order to make your program work correctly in Java, you have to declare TheFoo as volatile (or final, if you simply want to prevent such usage). Again, it looks like in Java the defaults are stacked against safe multithreading.
This is not a problem in D2, since shared implies volatile.
In my scheme, the default behavior of shared is different. It works like Java’s final. The code that tries to rebind the shared object (re-assign to the handle) would not compile. This is to prevent accidental lock-free programming. (If you haven’t noticed, the code that waits on the handle of TheFoo to switch from null to non-null is lock-free. The handle is not protected by any lock.) Unlike D2, I don’t want to make lock-free programming “easy,” because it isn’t easy. It’s almost like D2 is endorsing lock-free programming by giving the programmer a false sense of security.
So what do you do if you really want to spin on the handle? You declare your object lockfree.
lockfree Foo TheFoo;
lockfree implies shared (it doesn’t make sense otherwise), but it also makes the handle “volatile”. All accesses to it will be made sequentially consistent (on the x86, it means all stores will compile to xchg).
Note that lockfree is shallow–data members of TheFoo don’t inherit the lockfree qualification. Instead, they inherit the implied shared property of TheFoo.
It’s not only object handles that can be made lockfree. Other atomic data types like integers, Booleans, etc., can also be lockfree. A lockfree struct is also possible–it is treated as a tuple whose all elements are lockfree. There is no atomicity guarantee for the whole struct. Methods can be declared lockfree to turn off default synchronization.
Conclusion
Even the simplest case of sharing a global variable between threads is fraught with danger. My proposal inobtrusively eliminates most common traps. The defaults are carefully chosen to let the beginners avoid the pitfalls of multithreaded programming.
分享到:
相关推荐
Java-Multithreading-Tutorial:“正版编码器” YouTube频道Java多线程教程的源代码
4. 响应速度:在用户界面应用中,多线程可以使主线程专注于用户交互,而后台线程处理耗时任务,提高用户体验。 然而,多线程也带来了一些挑战和问题: 1. 线程安全:当多个线程访问和修改共享数据时,如果不加控制...
要么跑 php composer.phar require --prefer-dist grandmasterx/yii2-multithreading "*"或添加 "grandmasterx/yii2-multithreading": "*"到composer.json文件的 require 部分。用法安装扩展后,只需通过以下方式在...
4. **降低功耗**:利用ARM处理器的低功耗特性结合ThreadX的休眠机制,可以在保证功能性的前提下最大限度地减少能量消耗。 #### 五、具体实现案例 假设我们需要在一个基于ARM Cortex-M系列微控制器的嵌入式设备上...
"Multithreading: BostonGene的第一个测试任务"这个标题暗示我们,这可能是一个关于使用Java进行多线程编程的实践项目,旨在测试或训练开发者的技能。多线程允许程序同时执行多个独立的任务,提高系统的效率和响应性...
4. Condition接口:配合Lock使用,可以实现线程间的精确通信和等待条件。 四、线程池 Java的Executor框架允许创建线程池,可以更有效地管理和控制线程。ExecutorService、ThreadPoolExecutor和Executors是核心组件...
This book will teach you the finer points of multithreading and concurrency concepts and how to apply them efficiently in C++. Divided into three modules, we start with a brief introduction to the ...
npm install react-native-multithreading npx pod-install 需要包括的react-native-reanimated版本。 您可以自己打补丁,也可以等到它发布后再发布。 :warning: 警告:这仍然只是概念证明-请勿在生产中使用该库...
标题“multithreading:基本的多线程java网络”揭示了我们将讨论Java语言中的多线程技术及其在网络编程中的应用。多线程允许程序同时执行多个任务,提高资源利用率,从而提升整体性能。在网络编程中,多线程通常用于...
Python_multiprocessing_4_效率对比_multithreading,_multiprocessing_co
Chapter 4 of the "Operating System Concepts" course material focuses on the concept of threads, which play a crucial role in modern computing. Threads are the fundamental units of CPU utilization. ...
The authors explain the fundamentals of UNIX programming, focusing on communication, concurrency, and multithreading techniques: how they work, when to use them, and how to use them most effectively....
4. **同步机制**:当多个线程共享资源时,如缓存、内存等,需要有同步机制来避免数据冲突和不一致的问题。常见的同步机制包括锁、信号量、原子操作等。 5. **性能评估**:多线程架构的性能评估较为复杂,因为需要...
1,Real-Time Embedded Multithreading Using ThreadX and MIPS ...4,eetop.cn_Real-Time.Embedded.Multithreading-Using.ThreadX.and.MIPS 5,(CMP) Real-Time Embedded Multithreading--Using ThreadX & ARM
Mastering C++ Multithreading 英文epub 本资源转载自网络,如有侵权,请联系上传者或csdn删除 本资源转载自网络,如有侵权,请联系上传者或csdn删除
4. Lock接口和ReentrantLock类:提供比synchronized更精细的锁控制,支持公平锁、非公平锁、可重入锁、读写锁等。 四、线程状态 Java线程有以下几种状态:新建(New)、就绪(Runnable)、运行(Running)、等待/...
Mastering C++ Multithreading 英文无水印原版pdf pdf所有页面使用FoxitReader、PDF-XChangeViewer、SumatraPDF和Firefox测试都可以打开 本资源转载自网络,如有侵权,请联系上传者或csdn删除 查看此书详细信息...
4. **条件变量**:条件变量是一种高级同步工具,用于线程间的协作,例如等待特定条件满足后继续执行。 5. **读写锁**:在读多写少的情况下,读写锁(reader-writer lock)可以提高并发性能,允许多个线程同时读取...
《Multithreading Applications in Win32:The Complete Guide to Threads》是侯捷先生在2002年扫描整理的一本关于Windows系统下多线程编程的权威指南。这本书深入探讨了Win32环境下如何设计和实现多线程应用程序,...
4. **线程优先级** Java 线程有10个优先级,`Thread.MIN_PRIORITY`(1),`Thread.NORM_PRIORITY`(5,默认值),`Thread.MAX_PRIORITY`(10)。但实际调度依赖于操作系统,可能效果不明显。 5. **守护线程...