2015-07-29 3 views
2

Я не спрашиваю напрямую о мьютексах, хотя так или иначе связан.Как сделать взаимное исключение с помощью двух групп функций?

Существует 2 группы функций A и B. Когда функция из A работает, ни одна из функций в B не должна запускаться, и наоборот.

Существует несколько потоков, которые могут вызывать функции в A или B, и вышеприведенное должно быть выполнено.

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

+1

Какой язык вы используете - C или C++? Какую версию этого языка вы используете? На какой платформе или платформах вы работаете? Вы ищете код, используя средства, предоставляемые языковым стандартом, или вы ищете возможности, предоставляемые хостом O/S? И, в общем, почему вы считаете, что мьютекс не подходит? Один мьютекс может использоваться для защиты как набора A, так и множества B-функций; вы должны захватить этот мьютекс перед вызовом любой из подпрограмм в любом наборе. Конечно, это уменьшает параллелизм, но это кажется приемлемым. –

+1

@JonathanLeffler У меня есть полный доступ к потокам posix. Я пытаюсь найти способ свести к минимуму блокировку. – xiver77

+1

Просто, чтобы быть ясным - может быть 200 потоков, вызывающих функцию в группе A, до тех пор, пока нить не будет в функции в группе B, да? –

ответ

4

ОК, так что есть три действительные состояния:

Нет потоки не выполняющиеся функции в обеих группах.

Один или несколько потоков выполняются функции в группе А.

Один или несколько потоков стереосистеме и обновите функции в группе В.

Как и большинство сложных схем фиксирующих, это может управлять состоянием внутри замка. Состояние может быть перечислением, например (EgsFree, EgsAonly, EgsBonly), threadCount int, который отслеживает, сколько потоков любой из групп находится в защищенных функциях и контейнер для объектов синхронизации, по которым потоки, которые не были разрешены для запуска, ожидание.

Функции должны будут взаимодействовать, вызывая в заблокированное состояние при входе (идентифицируя свою группу с параметром) и при выходе.

Темы, которые обнаруживают при попытке ввода, что им не разрешено выполнять свою функцию, должны блокировать себя, поднимая событие/семафор, сохраняя его в контейнере и ожидая его после выхода из состояния блокировки. Это позволяет потокам, которые уменьшают счет до 0, (и поэтому снова устанавливают состояние в Efree), при выходе для итерации контейнера и подготовки всех ожидающих потоков.

Учитывая эту структуру, вы можете реализовать алгоритм против голодания или другие схемы управления, как вы пожелаете.

Данные состояния в замке грязные, и я хотел бы увидеть более простое решение этой проблемы. OTOH, он остается единственным безопасным способом комплексного управления ресурсами, о котором я знаю, на который можно положиться, чтобы работать и не иметь скрытого потенциала гонки/тупика. Если вам нужно слишком много думать о схеме блокировки, есть вероятность, что он в какой-то момент испортится :)

+0

Yup, алгоритм против голодания будет ключевым и постоянным кошмаром реализации. Это означает, что ОП не может избежать слишком много думать об этом. Поэтому я пришел к выводу, что, хотя я не знаю, что делает OP, он/она почти наверняка делает это неправильно. – user3386109

+0

^Я не предлагал, чтобы это было хорошим требованием, просто чтобы это можно было встретить;) Я понимаю, что если один поток вызывает групповую функцию, которая петли или блокирует в своем теле, это конец для всех потоков, которые пытаются функции вызова в другой группе :( –

+0

Согласовано, и поэтому я поддерживаю. Пластиковые пакеты тоже полезны, но они всегда приходят с предупреждением «Опасность удушья». Я полагал, что для этого ответа необходимо аналогичное предупреждение: «Опасность безумия ":) – user3386109

0

Я пытаюсь найти более открытый/простой ответ на вашу проблему, и вы можете сделать это:

class A 
{ 
public: 
    static void foo1(); 
    static void foo2(); 
private: 
    static std::shared_ptr<MyMutexClass> m_mmc; 
}; 

class B 
{ 
public: 
    static void foo3(); 
    static void foo4(); 
private: 
    static std::shared_ptr<MyMutexClass> m_mmc; 
}; 

Внутри вашего Foo fonctions, поставить охрану, используя статический мьютекс, представленный группой. Вам просто нужно найти способ инициализации A :: m_mmc и B :: m_mmc правильно.

3

Я писал этот код, прежде чем увидел ответ от Мартина Джеймса. Оказывается, это непроверенный код C++ 11, который иллюстрирует его ответ, поэтому, пожалуйста, подтвердите этот ответ.

Я думаю, что три позиционный переключатель с подсчетом ссылок может работать (не испытывалось класс)

struct ThreeWaySwitch 
{ 
    std::mutex mtx; 
    std::condition_variable cv; 
    enum State { IDLE, A, B } state; 
    int count; 

    ThreeWaySwitch() : state(IDLE), count(0) {} 
    void switchA() 
    { 
    std::unique_lock l(mtx); 
    while (state == B) cv.wait(l); 
    state = A; 
    ++count; 
    } 
    void switchB() 
    { 
    std::unique_lock l(mtx); 
    while (state == A) cv.wait(l); 
    state = B; 
    ++count; 
    } 
    void switchIdle() 
    { 
    { 
     std::unique_lock(mtx); 
     if (count == 0) return; // already idle 
     if (--count != 0) return; // not idle yet 
     state = IDLE; 
    } 
    cv.notify_all(); 
    } 
}; 

Таким образом, каждая функция вызывает switchA() на входе и switchIdle() на выходе и расп.щит() для B функции.

+1

Hmm .. Us genusses must wrok toogethr :) –

+0

@MartinJames: спасибо, я могу поместить код в свой ответ и удалить мой, если с тобой все в порядке. Ваше описание было похоже на то, что я имел в виду. – stefaanv

+1

Nah - оставьте его. Возможно, это не сработает. :) –

Смежные вопросы