Одессит
242 / 87 / 44
Регистрация: 30.12.2013
Сообщений: 316
Записей в блоге: 2
1

Синхронизация потоков

21.01.2020, 13:44. Показов 8326. Ответов 43
Метки нет (Все метки)

На собеседовании поставили такую задачу:
есть 3 потока, в каждом из которых вызывается функция, в которую передаётся символ. В бесконечном цикле фукция печатает символы. Нужно гарантировать, чтоб символы печатались строго по очереди (ABCABCABCABC...). Как не пытаюсь решить, выходит говнокод. Но есть подозрение, что можно решить каким-то изящным способом.
Код ниже - это начальное условие. Как его дополнить, чтоб решить задачу?

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void foo(const char ch)
{
    while (true) {
         cout << ch;
    }
}
 
int main()
{
    vector<thread> threads;
    threads.reserve(3);
    threads.emplace_back(foo, 'A');
    threads.emplace_back(foo, 'B');
    threads.emplace_back(foo, 'C');
 
    for(auto & t : threads) {
        t.join();
    }
 
    return 0;
}
Мой код
Кликните здесь для просмотра всего текста

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
#include <vector>
using namespace std;
 
mutex m, mCout;
static uint8_t queue = 0;
 
uint8_t addToQueue()
{
    lock_guard<mutex> l(m);
    return queue++;
}
 
bool isNext(uint8_t id) {
    static uint8_t next = 0;
    if(id == next) {
        ++next;
        if(next >= queue) {
            next = 0;
        }
        return true;
    } else {
        return false;
    }
}
 
void foo(const char ch)
{
    uint8_t id = addToQueue();
 
    while (true) {
        mCout.lock();
        if(isNext(id)) {
            cout << ch;
        }
        mCout.unlock();
    }
}
 
int main()
{
    vector<thread> threads;
    threads.reserve(3);
    threads.emplace_back(foo, 'A');
    threads.emplace_back(foo, 'B');
    threads.emplace_back(foo, 'C');
 
    for(auto & t : threads) {
        t.join();
    }
    return 0;
}
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
21.01.2020, 13:44
Ответы с готовыми решениями:

синхронизация потоков
проблема в следующем: есть 2 потока один считает некоторую сумму в цикле по столбцам матрицы...

Синхронизация потоков
Снова привет. Есть у меня код, который требуется раскидать на потоки. Ниже код: #include...

Синхронизация потоков
Добрый день. Как синхронизировать потоки, чтобы вывод в stdout был поочередным: foo bar foo bar?...

Синхронизация потоков в c++
Совершенно не понятно что не так и как правильно. Задача: Отсортировать массив целых чисел....

43
Эксперт С++
8703 / 4287 / 954
Регистрация: 15.11.2014
Сообщений: 9,723
21.01.2020, 14:06 2
Цитата Сообщение от kylroma Посмотреть сообщение
На собеседовании поставили такую задачу:
есть 3 потока, в каждом из которых вызывается функция, в которую передаётся символ. В бесконечном цикле фукция печатает символы. Нужно гарантировать, чтоб символы печатались строго по очереди (ABCABCABCABC...).
ты должен был уточнить у них: осознают ли они,
что ситуация, когда нужно делать что-то строго по очереди - диаметрально противоположна ситуации,
когда делать нужно параллельно?

правильный ответ:
"если вам нужна строгая очередность, тогда вам не нужна многопоточность".

если данные приходят по кусочкам (из разных тредов, либо ещё откуда то),
и использовать эти данные можно лишь при определенном условии,
тогда:
1. нужна функция предикат.
2. если функция предикат вернет истину - тупо берем и используем данные.
3. в условиях многопоточности не забываем расставить мутексы в нужных местах
4. PROFIT ????!!!!!
1
1550 / 875 / 179
Регистрация: 05.12.2015
Сообщений: 2,555
21.01.2020, 14:10 3
Цитата Сообщение от hoggy Посмотреть сообщение
"если вам нужна строгая очередность, тогда вам не нужна многопоточность".
Нужна строгая очередность вывода, например. Нужна ли многопоточность - зависит от объема вычичслений необходимых для этого вывода.
0
Нарушитель
7610 / 4176 / 966
Регистрация: 12.03.2015
Сообщений: 19,533
21.01.2020, 14:13 4
Каждый тред должен стоять на WaitForSingleObject() и дожидаться, когда предыдущий тред откроет ему Event, который держит ожидание. После прохода сквозь WaitForSingleObject() свой Event надо сбросить и открыть Event следующего треда. И так - повторять, пока Солнце не погаснет.
Цитата Сообщение от hoggy Посмотреть сообщение
ты должен был уточнить у них: осознают ли они,
что ситуация, когда нужно делать что-то строго по очереди - диаметрально противоположна ситуации,
когда делать нужно параллельно?
Категорически согласен. Для данной ситуации достаточно только одного треда.
0
1550 / 875 / 179
Регистрация: 05.12.2015
Сообщений: 2,555
21.01.2020, 14:16 5
Цитата Сообщение от Verevkin Посмотреть сообщение
WaitForSingleObject()
Это если ТС-у можно WINAPI. Иначе эвенты придется велосипедить на std::condition_variable-ах
0
Эксперт С++
8703 / 4287 / 954
Регистрация: 15.11.2014
Сообщений: 9,723
21.01.2020, 14:17 6
Цитата Сообщение от avgoor Посмотреть сообщение
Нужна строгая очередность вывода, например.
если ваш логгер не умеет логировать многопоточно(вывод в одном потоке),
тогда ваш логгер - узкое горлышко,
из-за которого все ваши треды на самом деле слоупочат друг за дружкой.
0
Нарушитель
7610 / 4176 / 966
Регистрация: 12.03.2015
Сообщений: 19,533
21.01.2020, 14:19 7
Цитата Сообщение от avgoor Посмотреть сообщение
Это если ТС-у можно WINAPI. Иначе эвенты придется велосипедить на std::condition_variable-ах

Цитата Сообщение от hoggy Посмотреть сообщение
из-за которого все ваши треды на самом деле слоупочат друг за дружкой.
Это и есть условие задачи, как не абсурдно это взучит.
0
1550 / 875 / 179
Регистрация: 05.12.2015
Сообщений: 2,555
21.01.2020, 14:29 8
Цитата Сообщение от hoggy Посмотреть сообщение
если ваш логгер не умеет логировать многопоточно
Причем тут логгер? К тому же если нужна именно очередность сообщений не зависящая от времени их обработки - как тут поможет этот многопоточный логгер.
Я вообще имел ввиду такую ситуацию: Допустим, есть распаралеливающийся тяжелый алгоритм, но в некоторых точках есть зависимость по данным от других потоков, причем, это не "барьеррная" синхронизация.
0
Эксперт С++
8703 / 4287 / 954
Регистрация: 15.11.2014
Сообщений: 9,723
21.01.2020, 14:35 9
Цитата Сообщение от avgoor Посмотреть сообщение
Причем тут логгер?
для примера:
Цитата Сообщение от avgoor Посмотреть сообщение
очередность вывода, например
Цитата Сообщение от avgoor Посмотреть сообщение
К тому же если нужна именно очередность сообщений не зависящая от времени их обработки - как тут поможет этот многопоточный логгер.
если нужна очередность, значит не нужна многопоточность.

Цитата Сообщение от avgoor Посмотреть сообщение
Я вообще имел ввиду такую ситуацию: Допустим, есть распаралеливающийся тяжелый алгоритм, но в некоторых точках есть зависимость по данным от других потоков, причем, это не "барьеррная" синхронизация.
значит это уже не "распаралеливающийся тяжелый алгоритм".
параллельные алгоритмы исполняются параллельно.
0
1550 / 875 / 179
Регистрация: 05.12.2015
Сообщений: 2,555
21.01.2020, 14:37 10
Цитата Сообщение от hoggy Посмотреть сообщение
значит это уже не "распаралеливающийся тяжелый алгоритм".
параллельные алгоритмы исполняются параллельно.
Один хрен быстрее чем в один поток и чем барьерная синхронизация.
0
Эксперт С++
8703 / 4287 / 954
Регистрация: 15.11.2014
Сообщений: 9,723
21.01.2020, 14:48 11
Цитата Сообщение от avgoor Посмотреть сообщение
Один хрен быстрее чем в один поток и чем барьерная синхронизация.
покажи код.
0
6571 / 4556 / 1843
Регистрация: 07.05.2019
Сообщений: 13,726
21.01.2020, 15:14 12
Лучший ответ Сообщение было отмечено kylroma как решение

Решение

Цитата Сообщение от kylroma Посмотреть сообщение
Код ниже - это начальное условие. Как его дополнить, чтоб решить задачу?
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const size_t N = 3
const char chs[N] = {'A', 'B', 'С'};
 
std::mutex mx;
volatile size_t idx = 0;
 
 
void ThreadProc(char ch)
{
    for(;;)
    {
        std::lock_guard lock(mx);
        if (chs[idx] == ch)
        {
            std::cout << ch << std::endl;
            if (idx == N - 1)
                idx = 0;
            else
                ++idx;
 
        }
    }
}
2
Just Do It!
3404 / 1872 / 621
Регистрация: 23.09.2014
Сообщений: 5,920
21.01.2020, 16:04 13
kylroma,
переделал вашу идею так чтобы работало:
+ чуток красивости на вывод
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
#include <vector>
using namespace std;
 
///----------------------------------------------------------------------------|
/// Менеджер очереди.
///----------------------------------------------------------------------------:
struct smyqueue
{   smyqueue() : q('C'){}
 
    char q;
    
    void end(const char _c)
    {   if(_c == 'C') cout << "\n";
        else          cout << " ";
        q = _c;
    }
    bool get(const char _c)
    {   std::this_thread::sleep_for(std::chrono::milliseconds(1));
        if((_c == q+1) || (_c == 'A' && q == 'C'))
        {   return true;
        }
        return false;
    }
}Q;
 
///----------------------------------------------------------------------------|
/// Многопоточка.
///----------------------------------------------------------------------------:
void foo(const char ch)
{   std::mutex mym;
    while (true)
    {   if(Q.get(ch))
        {   std::lock_guard<std::mutex> lock(mym);
            cout << ch;
            Q.end(ch);
        }
    }
}
 
///----------------------------------------------------------------------------|
/// Запуск.
///----------------------------------------------------------------------------:
int main()
{   vector<thread> threads;
    threads.reserve(3);
    threads.emplace_back(foo, 'A');
    threads.emplace_back(foo, 'B');
    threads.emplace_back(foo, 'C');
 
    for(auto& t: threads)
    {   t.join();
    }
    return 0;
}
Добавлено через 5 минут
Цитата Сообщение от XLAT Посмотреть сообщение
C++
24
if((_c == q+1) || (_c == 'A' && q == 'C'))
вот здесь выглядит как бы прибито гвоздями,
но так не обязательно делать.

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


Добавлено через 9 минут
Цитата Сообщение от hoggy Посмотреть сообщение
правильный ответ:
"если вам нужна строгая очередность, тогда вам не нужна многопоточность".
это неправильный ответ.

Вычисление идут параллельно,
но вывод по очереди.

При условии что время на вычисления будет в разы больше времени вывода, то мы получим ПРОФИТ!
2
Эксперт С++
8703 / 4287 / 954
Регистрация: 15.11.2014
Сообщений: 9,723
21.01.2020, 16:42 14
Цитата Сообщение от XLAT Посмотреть сообщение
Вычисление идут параллельно,
но вывод по очереди.
...

Цитата Сообщение от hoggy Посмотреть сообщение
если данные приходят по кусочкам (из разных тредов, либо ещё откуда то),
и использовать эти данные можно лишь при определенном условии,
тогда:
1. нужна функция предикат.
2. если функция предикат вернет истину - тупо берем и используем данные.
3. в условиях многопоточности не забываем расставить мутексы в нужных местах
4. PROFIT ????!!!!!
0
Эксперт С++
8382 / 6144 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
21.01.2020, 20:14 15
Цитата Сообщение от XLAT Посмотреть сообщение
Вычисление идут параллельно,
но вывод по очереди.
Нужно понимать что за вычисления и какова их длительность хотя бы приблизительно.
Т.е. задание идиотское и/или оторванное от реальности. Было бы немного приближенное к реальности можно было бы что-то советовать.

Обычно есть ввод и вывод и есть вычисления, ввод и вывод обычно синхронизируются а вычисления выполняются в потоке без синхронизации.
У Вас же вычилений нет, хотя именно для их ускорения и делается распараллеливание по потокам.
0
Just Do It!
3404 / 1872 / 621
Регистрация: 23.09.2014
Сообщений: 5,920
21.01.2020, 23:44 16
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
volatile
+1.

Итого:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <iostream>
#include <thread>
#include <vector>
//#include <atomic> // предопочел volatile
using namespace std;
 
///----------------------------------------------------------------------------|
/// Менеджер порядка вывода.
///----------------------------------------------------------------------------:
struct smyqueue
{   smyqueue() : q('C'){}
 
    volatile char q;
    //std::atomic<char> q{'C'};
    
    void end(const char _c)
    {   if(_c == 'C') cout << "\n";
        else          cout << " ";
        q = _c;
    }
    bool get(const char _c)
    {   return ((_c == q+1) || (_c == 'A' && q == 'C')) ? true : false;
    }
}Q;
 
///----------------------------------------------------------------------------|
/// Многопоточка.
///----------------------------------------------------------------------------:
void foo(const char ch)
{   while (true)
    {   if(Q.get(ch))
        {   cout << ch;
            Q.end(ch);
        }
    }
}
 
///----------------------------------------------------------------------------|
/// Запуск.
///----------------------------------------------------------------------------:
int main()
{   vector<thread> threads;
    threads.reserve(3);
    threads.emplace_back(foo, 'A');
    threads.emplace_back(foo, 'B');
    threads.emplace_back(foo, 'C');
 
    for(auto& t: threads)
    {   t.join();
    }
    return 0;
}
мутекс не нужен,
синхронизации и без него хватает.
1
Эксперт С++
8382 / 6144 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
22.01.2020, 00:20 17
Цитата Сообщение от XLAT Посмотреть сообщение
мутекс не нужен,
синхронизации и без него хватает.
Чего?
0
Just Do It!
3404 / 1872 / 621
Регистрация: 23.09.2014
Сообщений: 5,920
22.01.2020, 00:27 18
Цитата Сообщение от Avazart Посмотреть сообщение
Чего?
вы дюже охочи до устных объяснений?

Лучше кода ничто не объясняет:

К кауту в любой момент времени может иметь доступ только один тред!

Ах, да, пруфы?!

Смотрите код:
Синхронизация потоков
0
Don't worry, be happy
17767 / 10532 / 2034
Регистрация: 27.09.2012
Сообщений: 26,504
Записей в блоге: 1
22.01.2020, 08:58 19
XLAT, товарищ Avazart намекает, что volatile не имеет никакого отношения к синхронизации.
0
6571 / 4556 / 1843
Регистрация: 07.05.2019
Сообщений: 13,726
22.01.2020, 11:09 20
Цитата Сообщение от Croessmah Посмотреть сообщение
XLAT, товарищ Avazart намекает, что volatile не имеет никакого отношения к синхронизации.
В данном случае имеет. Код вроде вполне рабочий.
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
22.01.2020, 11:09
Помогаю со студенческими работами здесь

Синхронизация потоков
Есть статический класс к которому я хочу обращаться из разных потоков static class MyLog {...

Синхронизация потоков с++
Реализовать модуль создающий 4 балансировочных потока обеспечивающий 100% загрузку CPU (A,B,C,D)....

Синхронизация потоков - C++
Что это? Как это исправить? &quot;ConsoleApplication2.exe&quot; (Win32). Загружено...

Синхронизация потоков Event c++
Необходимо, чтобы нить t4 ждала события просчета времени &quot;time = 1000 * (getTime() - time);&quot; и...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2022, CyberForum.ru