2013-11-07 3 views
0

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

Я первый реализовали это при помощи Qt следующим образом:

Data globalData; 
QMutex mutex; 

void requestSend() // several such functions in other threads 
{ 
    mutex.lock(); // we want to change the data 
    globalData=fillData(); 
    invokeMethod(workerClass,"work",Qt::QueuedConnection); 
} 

void work() // a slot in a class instanced in worker thread 
{ 
    sendData(globalData); 
    mutex.unlock(); // data is now available to be changed 
} 

Это кажется разумным и даже работает, но потом я нашел это в документации QMutex:

аннулируются QMutex :: отпирания()

Открывает мьютексы. Попытка разблокировать мьютекс в другом потоке с тем, который заблокирован , приводит к ошибке. Разблокировка мьютекса, который не заблокирован, приводит к неопределенному поведению.

У меня есть два вопроса:

  1. Что причина такого ограничения, чтобы разблокировать в другом потоке? (и почему я не вижу ошибки, о которых говорит doc?)

  2. Что я должен использовать вместо QMutex для достижения того, что я пытаюсь сделать? Будет ли QWaitCondition быть адекватной заменой?

+0

Почему бы не использовать слот сигнала для передачи данных напрямую или даже передать его вместе с вызовом 'invokeMethod' –

+0

@ratchetfreak. Это будет связано с копированием данных, хотя я бы хотел его избежать. – Ruslan

ответ

1

Цель мьютекса - гарантировать, что только один поток может получить доступ к данным в любой момент времени. Поэтому на самом деле нет смысла блокировать один поток и разблокировать один и тот же мьютекс в другом.

Если вы обнаружите, что это работает, вам, вероятно, сейчас повезло, но это не значит, что это не вызовет у вас проблем, если время изменений потоков изменится.

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

Я предлагаю создать мьютекс вокруг записи данных и просто вызвать сигнал для отправки данных в поток, который будет отправлять данные. Находясь на разных потоках, данные будут скопированы в любом случае: -

void requestSend() // several such functions in other threads 
{ 
    QMutexLocker locker(&mutex); 
    globalData=fillData(); 
    emit SendData(globalData); // send signal to the thread which will send the data 
} 

Обратите внимание, что QMutexLocker используется для обеспечения блокировки освобождается, даже если исключение должно произойти.

Не будьте слишком обеспокоены копированием данных в сигналах и слотах; Qt очень эффективен и будет создавать «копию при записи» из-за implicit sharing, если вы используете его объекты-контейнеры. Даже если он должен сделать копию для передачи данных между потоками, вы не должны беспокоиться об этом, если не видите проблемы с производительностью.

Наконец, обратите внимание, что неявное совместное использование и многопоточность могут работать вместе, как вы можете read here.

+0

Не будет 'emit SendData();' return, не дождавшись завершения слота? В этом случае мы потеряем мьютекс, и это будет бесполезно. – Ruslan

+0

Это не должно быть проблемой. Когда вы излучаете сигнал, передайте объект данных в качестве параметра, и все будет нормально. Неявная модель совместного использования создаст неглубокую копию данных, но при необходимости произойдет глубокая копия, позволяющая получающему потоку получать правильные данные, а другой поток записывается в исходный контейнер. – TheDarkKnight

+0

ОК, теперь ваш код согласен с вашими словами :). Итак, в вашем примере 'mutex' защищает данные только от записи другими потоками, так как чтение больше не актуально, не так ли? – Ruslan

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