C++11 多线程 —— 通过原子变量实现同步操作

例子:从不同的线程读、写变量

std::vector<int> data;
std::atomic<bool> data_ready(false);

void reader_thread() {
	while (!data_ready.load()) {						// (1)
		std::this_thread::sleep_for(std::chrono::milliseconds(1));
	}
	std::cout << "The answer = " << data[0] << "\n";	// (2)
}
void writer_thread() {
	data.push_back(42);									// (3)
	data_ready = true;									// (4)
}

int main() {
	std::thread reader(reader_thread);
	std::thread writer(writer_thread);

	reader.join();
	writer.join();
}

解释It’s undefined behavior to have nonatomic reads (2) and writes (3) accessing the same data without an enforced ordering, so for this to work there must be an enforced ordering somewhere.

The required enforced ordering comes from the operations on the std::atomic<bool> variable data_ready; they provide the necessary ordering by virtue of the memory model relations happens-before and synchronizes-with.

  1. The write of the data (3) happens-before the write to the data_ready flag (4), and the read of the flag (1) happens-before the read of the data (2).
  2. When the value read from data_ready (1) is true, the write synchronizes-with that read, creating a happens-before relationship. Because happens-before is transitive, the write to the data (3) happens-before the write to the flag (4), which happens-before the read of the true value from the flag (1), which happens-before the read of the data (2), and you have an enforced ordering: the write of the data happens-before the read of the data and everything is OK.
    在这里插入图片描述

1. synchronizes-with 关系

  • 只有原子操作间才有如此关系)The synchronizes-with relationship is something that you can get only between operations on atomic types.

  • 基本思想:原子写操作与原子读操作同步)The basic idea is this: a suitably tagged atomic write operation W on a variable x synchronizes-with a suitably tagged atomic read operation on x that reads the value stored by either that write (W), or a subsequent atomic write operation on x by the same thread that performed the initial write W, or a sequence of atomic read-modify-write operations on x (such as fetch_add() or compare_exchange_weak()) by any thread, where the value read by the first thread in the sequence is the value written by W.

    简而言之)If thread A stores a value and thread B reads that value, there’s a synchronizes-with relationship between the store in thread A and the load in thread B.

    注意:前提是原子读操作和原子写操作是 “suitably tagged” 的,当然,默认指定的内存序也算。


2. happens-before 关系

  • It specifies which operations see the effects of which other operations.

  • 单线程内)For a single thread, it’s largely straightforward: if one operation is sequenced before another, then it also happens-before it. This means that if one operation (A) occurs in a statement prior to another (B) in the source code, then A happens-before B.

    同一条语句内的操作是无序的!If the operations occur in the same statement, in general there’s no happens-before relationship between them, because they’re unordered. 如,

    void foo(int a,int b) {
    	std::cout<<a<<","<<b<<std::endl;
    }
    
    int get_num() {
    	static int i=0;
    	return ++i;
    }
    
    int main() {
    	foo(get_num(), get_num());		// Calls to get_num() are unordered:它们在同一条语句
    }
    

    上述可能输出 “1,2” 或 “2, 1”。

    例外的情况)There are circumstances where operations within a single statement are sequenced such as where the built-in comma operator is used or where the result of one expression is used as an argument to another expression.
    But in general, operations within a single statement are nonsequenced, and there’s no sequenced-before (and thus no happens-before) relationship between them. Of course, all operations in a statement happen before all of the operations in the next statement.

  • 多线程间)If operation A on one thread inter-thread happens-before operation B on another thread, then A happens-before B.

    If operation A in one thread synchronizes-with operation B in another thread, then A inter-thread happens-before B. It’s also a transitive relation: if A inter-thread happens-before B and B inter-thread happens-before C, then A inter-thread happens-before C.

    inter-thread happens-before结合sequenced-before)Inter-thread happens-before also combines with the sequenced-before relation: if operation A is sequenced before operation B, and operation B inter-thread happens-before operation C, then A inter-thread happens-before C. Similarly, if A synchronizes-with B and B is sequenced before C, then A inter-thread happens-before C.
    These two together mean that if you make a series of changes to data in a single thread, you need only one synchronizes-with relationship for the data to be visible to subsequent operations on the thread that executed C.

猜你喜欢

转载自blog.csdn.net/fcku_88/article/details/88549731