1

Тема: Незрозуміло навіщо lock у потоці, що готує дані?

Якщо у нас один писач і один читач, то що поганого може статись якщо локу у thread1 не буде?

void thread1()
{
  // підготувати щось для thread2
  std::cout << "<return>" << std::endl;
  std::cin.get();
  // повідомити thread1, що все готово
  {
    std::lock_guard<std::mutex> lg(readyMutex); // НАВІЩО ЦЕЙ lock
    readyFlag = true;
  } // звільнити lock
  readyCondVar.notify_one();
}

void thread2()
{
  // чекати доки thread1 підготує щось (readyFlag стане true)
  {
    std::unique_lock<std::mutex> ul(readyMutex);
    readyCondVar.wait(ul, []{ return readyFlag; });
  } // звільнити lock
  // зробити, що треба коли все вже підготовлене
  std::cout << "done" << std::endl;
}

int main()
{
  auto f1 = std::async(std::launch::async,thread1);
  auto f2 = std::async(std::launch::async,thread2);
}
Подякували: leofun011

2

Re: Незрозуміло навіщо lock у потоці, що готує дані?

Погане станеться те, що прога не знає коли система потоку дасть квант часу, а коли забере, може то навіть під час писання масиву, дані будуть зіпсовані.

3

Re: Незрозуміло навіщо lock у потоці, що готує дані?

Загалом я зрозумів. Порядок операцій може змінитись.

4

Re: Незрозуміло навіщо lock у потоці, що готує дані?

то що поганого може статись якщо локу у thread1 не буде?

- дедлок наприклад
http://www.bogotobogo.com/cplusplus/C11 … adLock.php

5 Востаннє редагувалося Yola (01.05.2016 17:02:48)

Re: Незрозуміло навіщо lock у потоці, що готує дані?

-=ЮрА=- написав:

то що поганого може статись якщо локу у thread1 не буде?

- дедлок наприклад
http://www.bogotobogo.com/cplusplus/C11 … adLock.php

Ніякого взаємного блокування тут бути не може, бо у мене тільки один м'ютекс. В цьому конкретному випадку єдина можлива проблема це те, що readyFlag = true; виконається перед тим як щось буде підготовлене для thread2. Більше того, гадаю, що навіть це стане проблемою лише якщо conditional_variable помилково спрацює/прокинеться.

6 Востаннє редагувалося iovchynnikov (01.05.2016 19:24:06)

Re: Незрозуміло навіщо lock у потоці, що готує дані?

Yola написав:
-=ЮрА=- написав:

то що поганого може статись якщо локу у thread1 не буде?

- дедлок наприклад
http://www.bogotobogo.com/cplusplus/C11 … adLock.php

Ніякого взаємного блокування тут бути не може, бо у мене тільки один м'ютекс.

А як, відповідно до Вашого твердження, відбувається дедлоки? Коли є 2 м'ютекси?

7 Востаннє редагувалося -=ЮрА=- (01.05.2016 20:08:23)

Re: Незрозуміло навіщо lock у потоці, що готує дані?

Прихований текст

Yola не приймай особисто(хоча ні приймай особисто)я помітив що ти щось питаєш на форумі тобі намагаються допогти, а ти тільки фекаєш та демонструєш мовляв знаєш більше і у такуму плані, повір - це поганий тон у будь якій сфері...Проте ми тут про інше - дедлок відбувається, коли два потоки одночасно намагаються дістатися до одного ресурсу(у твоєму випадку кондішин флагу), у разі прибирання локу у першій треді ти маєш неконтрольований доступ до кондішин флагу з боку першої, і лок у другій просто на має сенсу(бо мьютекс завжди буде зайнатий саме нею. При цьому якщо ти не вкажеш для кондішин флагу специфікатор volatile, то можешь взагалі отримати "оптимізацію" від компялтора, коли він для "простоти" розкидає флаг на дві ячійки чи навіть регістра, один для першої, другий для другої треди і будешь працювати взагалі у несинхронному режимі. Якщо проставиш volatile будеш лише час від часу мати або запис на те шо читається або дедлок(!)), але нашо ото мені пояснювати щось людині яка все знає та певна у всьому.

Подякували: iovchynnikov, leofun012

8 Востаннє редагувалося Yola (03.05.2016 08:49:51)

Re: Незрозуміло навіщо lock у потоці, що готує дані?

-=ЮрА=- написав:

то що поганого може статись якщо локу у thread1 не буде?

- дедлок наприклад
http://www.bogotobogo.com/cplusplus/C11 … adLock.php

Сама назва взаємне блокування має на увазі, що потоки блокують один одного взаємно.

От визначення з Вікіпедії:

У паралельному програмуванні, взаємне блокування - це ситуація коли дві або більше дії, що конкурують очікують завершення одна одної, і отже жодна не завершується.

Якщо не використовувати мютекс, маємо ситуацію, коли thread1 гарантовано закінчиться, отже це не взаємне блокування.

9

Re: Незрозуміло навіщо lock у потоці, що готує дані?

iovchynnikov написав:
Yola написав:

Ніякого взаємного блокування тут бути не може, бо у мене тільки один м'ютекс.

А як, відповідно до Вашого твердження, відбувається дедлоки? Коли є 2 м'ютекси?

Саме так, два м'ютекси або інші примітиви синхронізації. Якщо ви синхронізуєте потоки лише одним м'ютексом чи чимось іншим як-от семафор чи якійсь власноруч писаний примітив. інакше один з процесів, той що захопив м'ютекс, завжди має змогу завершитись і звільнити ресурс.

10

Re: Незрозуміло навіщо lock у потоці, що готує дані?

-=ЮрА=- написав:

Yola не приймай особисто(хоча ні приймай особисто)я помітив що ти щось питаєш на форумі тобі намагаються допогти, а ти тільки фекаєш та демонструєш мовляв знаєш більше і у такуму плані, повір - це поганий тон у будь якій сфері...

Згоден, це поганий тон, але я таки знаю більше ніж ви. Ну так сталось, без образ. Я буду радий якщо ви з часом переженете. Я кажу це абсолютно щиро.

-=ЮрА=- написав:

Проте ми тут про інше - дедлок відбувається, коли два потоки одночасно намагаються дістатися до одного ресурсу

Ні, це не дедлок. Розглянемо приклад:
A: запит на ресурс R. Початок роботи з ресурсом.
B: запит на ресурс R. Перехід в заблокований стан.
A: Завершення роботи з ресурсом. Звільнення ресурсу.
B: Прокидання, отримання ресурсу. Використання ресурсу. Завершення роботи.

Отже, два потоки - A і B, намагались одночасно отримати доступу до ресурсу, але взаємного блокування не відбулось.


-=ЮрА=- написав:

(у твоєму випадку кондішин флагу), у разі прибирання локу у першій треді ти маєш неконтрольований доступ до кондішин флагу з боку першої, і лок у другій просто на має сенсу(бо мьютекс завжди буде зайнатий саме нею. При цьому якщо ти не вкажеш для кондішин флагу специфікатор volatile, то можешь взагалі отримати "оптимізацію" від компялтора, коли він для "простоти" розкидає флаг на дві ячійки чи навіть регістра, один для першої, другий для другої треди і будешь працювати взагалі у несинхронному режимі. Якщо проставиш volatile будеш лише час від часу мати або запис на те шо читається або дедлок(!)), але нашо ото мені пояснювати щось людині яка все знає та певна у всьому.

Тут все стало зовсім незрозуміло *SCRATCH* , можливо ви дійсно знаєте більше ніж я :D .
Скажу лише, що якщо вважати, і це тут буде логічним припущенням, що readyFlag це bool чи навіть __int128, то у конкретно цьому випадку немає значення як його розкидає по регістрах чи ще десь ;)

11 Востаннє редагувалося -=ЮрА=- (02.05.2016 14:54:40)

Re: Незрозуміло навіщо lock у потоці, що готує дані?

Прихований текст

але Я таки знаю більше ніж ви. Ну так сталось, без образ.Я буду радий якщо ви з часом переженете.

це bool чи навіть __int128, то у конкретно цьому випадку немає значення як його розкидає по регістрах чи ще десь

  Добре :)

12 Востаннє редагувалося Yola (03.05.2016 08:51:30)

Re: Незрозуміло навіщо lock у потоці, що готує дані?

-=ЮрА=- написав:

це bool чи навіть __int128, то у конкретно цьому випадку немає значення як його розкидає по регістрах чи ще десь

  Добре :)

Наведена програма перевіряє чи змінився false на true. Тому для логічної правильності програми не має значення чи запишеться значення одразу, чи запис рознесеться на два кванти часу.

Якби змінна була лічильником чи містила іншу складнішу інформацію, тоді неповний запис становив би проблему, але тут така проблема не виникає.

13 Востаннє редагувалося 0x9111A (04.05.2016 13:32:56)

Re: Незрозуміло навіщо lock у потоці, що готує дані?

cppreference:

Even if the shared variable is atomic, it must be modified under the mutex in order to correctly publish the modification to the waiting thread.

Це потрібно щоб унеможливити наступний сценарій:

void thread1()
{
    readyFlag = true;
    readyCondVar.notify_one();
}
 
void thread2()
{
    std::unique_lock<std::mutex> ul(readyMutex);
    if (readyFlag == false) {
    // в цей момент спрацьовує readyCondVar.notify_one() з thread1
    // наступний виклик не розблокується, так як сигнал уже прийшов і пішов
        readyCondVar.wait(ul);
    }
}

В цьому варіанті є гарантія, що або "readyFlag = true;" виконається перед "if (readyFlag == false)", або
readyCondVar.notify_one() виконається після readyCondVar.wait(ul).

void thread1()
{
    {
        std::lock_guard<std::mutex> lg(readyMutex);
        readyFlag = true;
    }

    readyCondVar.notify_one();
}
 
void thread2()
{
    std::unique_lock<std::mutex> ul(readyMutex);
    if (readyFlag == false) {
        readyCondVar.wait(ul);
    }
}
Подякували: Yola, leofun012

14

Re: Незрозуміло навіщо lock у потоці, що готує дані?

0x9111A написав:
        readyCondVar.wait(ul);

Так, якщо змінити мій код таким чином, то дійсно може відбутися блокування одного з потоків.

15 Востаннє редагувалося 0x9111A (04.05.2016 13:00:36)

Re: Незрозуміло навіщо lock у потоці, що готує дані?

Ваш код так і працює, бо

void wait( std::unique_lock<std::mutex>& lock, Predicate pred );

працює як

while (!pred()) {
    wait(lock);
}

if з while на швидку руку сплутав, але в нашому випадку то не важливо
Між іншим while та й взагалі такий варіант wait зроблено для вирішення наступної проблеми

Подякували: Yola, leofun012

16

Re: Незрозуміло навіщо lock у потоці, що готує дані?

0x9111A написав:
while (!pred()) {
    wait(lock);
}

Дуже дякую! Я мав хибне уявлення про ймовірну реалізацію conditional_variable.