2009-08-02 4 views
6

Если мьютекс определен внутри функции, применяется ли его блокировка к функциям, вызываемым из этой функции? т.е.Связана ли блокировка мьютекса с вызываемыми функциями?

void f() { 
    Mutex mutex; 
    g(); 
} 

Сохраняется ли блокировка в отношении любых модификаций данных в g()?

Кроме того, могу ли я сказать, что блокировка, определенная в методе класса, применима только к конкретным экземплярам этого класса? Значение:

Class Foo; 
Foo foo1, foo2; 
(In thread 1) foo1.bar(); 
(In thread 2) foo2.bar(); 

Может ли каждый вызов быть встречным одновременно?

Было бы неплохо, если бы кто-нибудь мог объяснить/указать ссылки, которые объясняют механизм затвора. Благодаря! В настоящее время я работаю с библиотекой потоков Qt, если эта информация помогает.

ответ

15

В вашем примере вы фактически не блокируете мьютексы, поэтому он не будет препятствовать одновременному доступу к различным функциям. Также вы объявляете мьютекс локально внутри функции, так что каждый вызов функции использует другой локальный объект 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. 
    } 

    // ... 
}; 

В этом случае mutex блокируется до тех пор, как ml существует, так и во время пребывания потока внутри g(). Если бы другой поток вызывал f() за это время, он блокировал бы при создании своего объекта ml до тех пор, пока первый поток не оставит функцию, а новый поток может получить блокировку на mutex.

+1

+1 с использованием QT api –

7

Мьютекс - это то, что вы хватаете, и остановит любые другие потоки, пытаясь схватить его, пока вы не отпустите его от захватывающей нити.

В вашем вопросе у вас есть функция f, выделяющая экземпляр Mutex. Этого недостаточно, чтобы заблокировать его. Вам нужно специально называть 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(), то вы синхронизированы на этом доступе.

Для второй части вашего вопроса, если мьютекс является атрибутом экземпляра, то они будут двумя разными взаимными обозначениями. Вам нужно будет объявить и создать экземпляр экземпляра класса mutex и обратиться к нему за вашей блокировкой. В этом случае любая попытка вызова метода в классе, который блокирует мьютез класса, будет эффективно синхронизирована, а это означает, что ни один из двух потоков не будет выполнять этот метод вместе.

Например (я не имею Qt, поэтому я не могу скомпилировать этот код, и я прекратил кодирование с ней 2 года назад, так что он не мог работать)

class Foo { 
public: 
    void method(void) { 
     mutex.lock(); 
     cout << "method called"; 
     // long computation 
     mutex.unlock(); 
    } 

private: 
    QMutex mutex; 
}; 

Хорошо, в этом случае, предположим, что у вас есть два потока: 1 и 2 и два экземпляра класса Foo, a и b. Предположим, что поток 1 вызывает a.method(), а thread 2 вызывает b.method(). В этом случае два мьютекса представляют собой разные экземпляры, поэтому каждый поток будет выполнять вызов самостоятельно и запускаться параллельно.

Предположим, у вас есть два потока: 1 и 2 и один экземпляр класса Foo, который разделяется между двумя потоками. если поток 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; 

В этом случае мьютекс является статической переменной класса. У вас есть только один экземпляр мьютекса для каждого экземпляра объекта. Предположим, что у вас была такая же ситуация, как в первом случае: два потока и два экземпляра. В этом случае, когда второй поток пытается вызвать b.method(), ему придется дождаться, когда a.method() будет завершен первым потоком, поскольку блокировка теперь уникальна и распределена между всеми экземплярами вашего класса.

Для получения дополнительной информации, Qt имеет хороший учебник по многопоточности

https://doc.qt.io/qt-5/threads.html

+0

Извините, я сделал очень плохую попытку псевдокода. Просто исправил это. Ваш ответ для первой части был именно тем, о чем мне было интересно, спасибо! – int3

2

Ваш мьютекс instatiated локально, на стеке. Таким образом, вызов f() из одного потока будет блокировать свой экземпляр мьютекса, соответствующий. Любой другой вызов f() из другого потока блокирует его. Таким образом, состояние гонок может происходить с данными, доступными из g()! Даже жестко вы называете это в том же экземпляре класса:

MyClass foo; 
(In thread 1) foo->f(); 
(In thread 2) foo->f(); 

Как лучше управлять блокировкой зависит от того, что вы хотите сделать. Согласно тому, что вы сказали, я полагаю, что лучшей политикой было бы непосредственно изменить реализацию g(): она должна блокировать мьютекс, объявленный как глобальный, например, или как статический в g() для совместного использования между любыми вызовами g(). Насколько я понимаю, вы хотите заблокировать свои данные во всем мире?

+0

, так что если у меня есть данные, которые я хочу заблокировать в определенных экземплярах Foo :: f(), должен ли я создать мьютекс как нестатические данные класса? – int3

+0

Ну, я думаю, это должно сделать трюк. Тогда любой вызов из экземпляра «foo» из любого потока в моем примере предотвратит одновременный доступ. (Но не из двух разных экземпляров, конечно) –