ミューテックスのロックは、呼び出された関数にも適用されますか?
-
10-07-2019 - |
質問
ミューテックスが関数内で定義されている場合、そのロックはその関数から呼び出された関数に適用されますか?すなわち
void f () {
Mutex mutex;
g();
}
ロックはg()のデータ変更に引き続き適用されますか?
また、クラスメソッドで定義されたロックは、そのクラスの特定のインスタンスにのみ適用されると言ってもいいですか?意味:
Class Foo;
Foo foo1, foo2;
(In thread 1) foo1.bar();
(In thread 2) foo2.bar();
各呼び出しは同時に発生できますか?
ミューテックスの背後にあるメカニズムを説明するリンクを誰かが説明したり指摘したりすることができれば、素晴らしいボーナスになります。ありがとう!その情報が役立つ場合、私は現在Qtスレッドライブラリで作業しています。
解決
この例では、実際にはミューテックスをロックしないため、異なるスレッドが同時に関数にアクセスすることを妨げません。また、各関数呼び出しが異なるローカルミューテックスオブジェクトを使用するように、ミューテックスを関数内でローカルに宣言します。このmutexがロックされていても、各関数呼び出しは異なるmutexオブジェクトをロックし、同時アクセスを妨げません。
より良い戦略は、次のような設定です。
class A {
QMutex mutex;
void f() {
QMutexLocker ml(mutex); // Acquire a lock on mutex
g();
// The lock on the mutex will be released when ml is destroyed.
// This happens at the end of this function.
}
// ...
};
この場合、 ml
が存在する限り mutex
はロックされるため、スレッドが g()
内で費やしている間もロックされます。この間に別のスレッドが f()
を呼び出すと、最初のスレッドが関数を離れ、新しいスレッドがロックを取得できるまで、その ml
オブジェクトの作成をブロックします。 mutex
で。
他のヒント
ミューテックスはあなたがつかむものであり、つかむスレッドから解放するまで他のスレッドがそれをつかもうとするのを止めます。
質問では、Mutexインスタンスを割り当てる関数fがあります。それだけではロックできません。 mutex.lock()を具体的に呼び出す必要があります(Qtでは一般的ですが、pthreadを使用しない限り、その場合はpthread_mutex_lockを使用し、プラットフォームに依存する低レベルのものを楽しんでください。Qtはそれを非常にうまく抽象化します)。
Qtを使用した例
void MyClass::doStuff( int c )
{
mutex.lock();
a = c;
b = c * 2;
mutex.unlock();
}
ロックを取得すると、ロックを取得したスレッドからg()の呼び出しが行われるため、g()を呼び出していないことを想定して、その呼び出しで単独で呼び出しますコードの他の部分の他のスレッドから。ロックは、他のすべてのスレッドを停止するという意味ではありません。ロックが解除されるまで、同じロックを取得しようとするスレッドを停止します。
それがスレッドがg()に到達する唯一の方法である場合、そのアクセスで同期されます。
質問の2番目の部分では、mutexがインスタンス属性の場合、2つの異なるミューテックスになります。クラスmutexインスタンスを宣言およびインスタンス化し、ロックするためにそれを参照する必要があります。その場合、クラスミューテックスをロックするクラスのメソッドを呼び出そうとすると、効果的に同期されます。つまり、2つのスレッドがそのメソッドを一緒に実行することはありません。
たとえば(Qtがないため、このコードをコンパイルできず、2年前にコーディングを停止したため、動作しませんでした)
class Foo {
public:
void method(void) {
mutex.lock();
cout << "method called";
// long computation
mutex.unlock();
}
private:
QMutex mutex;
};
Ok、この場合、2つのスレッド1と2、およびクラスFooの2つのインスタンスaとbがあるとします。スレッド1がa.method()を呼び出し、スレッド2がb.method()を呼び出すとします。この場合、2つのミューテックスは異なるインスタンスであるため、各スレッドは独立して呼び出しを実行し、並行して実行されます。
1と2の2つのスレッドと、2つのスレッド間で共有されるクラスFooの1つのインスタンスがあるとします。スレッド1がa.method()を呼び出し、次にスレッド2がa.method()を呼び出す場合、スレッド2は停止し、相互排他ロックが解放されるまで待機します。
最後に、
class Foo {
public:
void method(void) {
mutex.lock();
cout << "method called";
// long computation
mutex.unlock();
}
private:
static QMutex mutex;
};
QMutex Foo::mutex;
この場合、ミューテックスはクラスの静的変数です。各オブジェクトインスタンスに対して、ミューテックスのインスタンスは1つしかありません。上記の最初のケースと同じ状況、つまり2つのスレッドと2つのインスタンスがあるとします。この場合、2番目のスレッドがb.method()を呼び出そうとすると、最初のスレッドがa.method()を完了するまで待機する必要があります。ロックは一意であり、クラスのすべてのインスタンス間で共有されるためです。
詳細については、Qtにマルチスレッド化に関する素晴らしいチュートリアルがあります
ミューテックスは、スタック上でローカルにインスタンス化されます。そのため、1つのスレッドからf()を呼び出すと、mutexの own インスタンスがロックされます。別のスレッドからf()を呼び出すと、それ自体がロックされます。したがって、g()からアクセスされるデータで競合状態が発生する可能性があります。同じクラスインスタンスで呼び出すことさえ難しい:
MyClass foo;
(In thread 1) foo->f();
(In thread 2) foo->f();
ロックをより適切に処理する方法は、何をしたいかによって異なります。あなたが言ったことによると、より良いポリシーはg()実装を直接変更することだと思います:例えばグローバルとして宣言されたミューテックスをロックするか、g()の呼び出し間で共有されるg()で静的であるとロックする必要があります。私が理解している限り、データをグローバルにロックしたいですか?