元のテキストはhttps://segmentfault.com/a/1190000006614695で複製されています。
並行プログラミング、スレッドビデオリソース
複数のスレッドが同じリソースにアクセスする場合、データの整合性を確保するために、最も簡単な方法はミューテックス(相互排除ロック)を使用することです。
cppreferenceの導入を引用する:
ミューテックスクラスは、
共有データが複数の
スレッドによって同時にアクセスされるのを防ぐために使用できる同期プリミティブです。
ミューテックス1
ミューテックスを直接操作します。つまり、ミューテックスのロック/ロック解除機能を直接呼び出します。
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
std::mutex g_mutex;
int g_count = 0;
void Counter() {
g_mutex.lock();
int i = ++g_count;
std::cout << "count: " << i << std::endl;
// 前面代码如有异常,unlock 就调不到了。
g_mutex.unlock();
}
int main() {
const std::size_t SIZE = 4;
// 创建一组线程。
std::vector<std::thread> v;
v.reserve(SIZE);
for (std::size_t i = 0; i < SIZE; ++i) {
v.emplace_back(&Counter);
}
// 等待所有线程结束。
for (std::thread& t : v) {
t.join();
}
return 0;
}
残念ながら、STLはスレッドのグループを表すboost :: thread_groupなどのツールを提供していません。std:: vectorも目標を達成できますが、コードは十分に簡潔ではありません。
ミューテックス2
lock_guardを使用して、自動的にロックおよびロック解除します。原理は、スマートポインタと同様にRAIIです。
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
std::mutex g_mutex;
int g_count = 0;
void Counter() {
// lock_guard 在构造函数里加锁,在析构函数里解锁。
std::lock_guard<std::mutex> lock(g_mutex);
int i = ++g_count;
std::cout << "count: " << i << std::endl;
}
int main() {
const std::size_t SIZE = 4;
std::vector<std::thread> v;
v.reserve(SIZE);
for (std::size_t i = 0; i < SIZE; ++i) {
v.emplace_back(&Counter);
}
for (std::thread& t : v) {
t.join();
}
return 0;
}
ミューテックス3
unique_lockを使用して、自動的にロックおよびロック解除します。
Unique_lockの原理はlock_guardと同じですが、より多くの関数を提供します(たとえば、条件変数と組み合わせて使用できます)。
注:mutex :: scoped_lockは、実際にはunique_lockのtypedefです。
unique_lockとlock_guardの詳細な比較については、StackOverflowを参照してください。
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
std::mutex g_mutex;
int g_count = 0;
void Counter() {
std::unique_lock<std::mutex> lock(g_mutex);
int i = ++g_count;
std::cout << "count: " << i << std::endl;
}
int main() {
const std::size_t SIZE = 4;
std::vector<std::thread> v;
v.reserve(SIZE);
for (std::size_t i = 0; i < SIZE; ++i) {
v.emplace_back(&Counter);
}
for (std::thread& t : v) {
t.join();
}
return 0;
}
ミューテックス4
出力ストリームには別のミューテックスを使用してください。
これは、IOストリームがスレッドセーフではないために行われます。
IOが同期されていない場合、この例の出力は次のようになります。
count == count == 2count == 41
count == 3
次の出力ステートメントで:
std::cout << "count == " << i << std::endl;
出力 "count =="とiの2つのアクションはアトミックではなく、他のスレッドによって中断される可能性があります。
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
std::mutex g_mutex;
std::mutex g_io_mutex;
int g_count = 0;
void Counter() {
int i;
{
std::unique_lock<std::mutex> lock(g_mutex);
i = ++g_count;
}
{
std::unique_lock<std::mutex> lock(g_io_mutex);
std::cout << "count: " << i << std::endl;
}
}
int main() {
const std::size_t SIZE = 4;
std::vector<std::thread> v;
v.reserve(SIZE);
for (std::size_t i = 0; i < SIZE; ++i) {
v.emplace_back(&Counter);
}
for (std::thread& t : v) {
t.join();
}
return 0;
}