Вопрос о многопоточности C

#c #multithreading

#c #многопоточность

Вопрос:

Недавно я создавал очень простое приложение, которое просто выводило матричный эффект на консоль. Итак, я сделал это в очень простой в использовании функции void drawLine(int startX, int startY, int lineLength,int speed); . Проблема сейчас в том, что за раз выводится только одна строка. Конечно, мне нужно сделать это приложение многопоточным, но я столкнулся с трудностями, потому что я никогда не делал этого на C , только на C #, а на C # это очень легко сделать по сравнению с C .

Я провел некоторое исследование и попытался создать 3 потока с помощью CreateThread , а затем запустить их с помощью WaitForMultipleObjects . Но вывод очень странный и не кажется правильным. И это также приводит меня к следующей проблеме, даже если это будет работать правильно. Представьте, что я хочу запустить более 15 строк на своей консоли, означает ли это, что мне нужно создать 15 разных потоков?

Обратите внимание, что это не что-то важное, это просто то, что я только что создал, потому что мне было скучно, а также потому, что я хочу научиться работать с потоками на C . Я, конечно, могу использовать библиотеки boost, но я хочу создать пример для себя, не используя его.

Вот скриншот с 1 потоком только для того, чтобы сделать его более понятным: введите описание изображения здесь

Комментарии:

1. Странный вывод может быть признаком того, что — по крайней мере — один из ваших объектов не является потокобезопасным.

2. С какой стати вам нужно делать это многопоточным? Консольный ввод-вывод, скорее всего, в любом случае будет заблокирован (одновременно печатать может только один поток).

3. Это нехорошо, тогда будет невозможно создать что-то подобное. Хотя в C # это работало просто отлично…

4. он не говорит, что это не сработает, просто что ничего не получится, если сделать его многопоточным (что верно и в C #).

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

Ответ №1:

Эта проблема не имеет ничего общего с обработкой потоков — или, скорее, эта проблема абсолютно не требует обработки потоков. Простой объектно-ориентированный дизайн должен легко позволять вам рисовать несколько линий во фрейме — как, по вашему мнению, однопоточные игры рисуют тысячи или более вершин во фрейме?

Что еще более важно, только последние (Windows Vista или более поздние) средства визуализации для Windows допускают многопоточную визуализацию, то есть Direct2D / Direct3D11. Другие средства визуализации, такие как D3D9, содержат внутренние блокировки, которые предотвращают многопоточный рендеринг, поскольку их время выполнения и графические драйверы не могут с этим справиться.

Если у вас есть средство визуализации, защищенное от параллелизма, такое как Direct2D, то визуализация из нескольких потоков должна быть относительно тривиальной — и вам не следует использовать собственный Windows threading API. Я вижу, что вы используете Visual Studio 2010 — используйте среду выполнения с параллелизмом. WinAPI предоставляет только примитивы для обработки потоков — их прямое использование было бы сродни написанию на ассемблере. По крайней мере, используйте boost::thread . Многопоточность на самом деле связана не с реализацией, а с хорошим дизайном, и дизайн не имеет значения, какую библиотеку вы используете для его реализации.

Редактировать: Подождите минутку, вы используете консоль? Это совсем не потокобезопасно. Это безумие. Консоль существует для самого простого ввода-вывода, а не для этого. Наиболее вероятно, что C # просто синхронизирует это для вас, а C — нет.

Ответ №2:

Функция DrawLine, вероятно, представляет собой один цикл с командой для позиционирования курсора, после чего выполняется печать символа (если нет, то покажите нам код функции). Эти две инструкции должны выполняться последовательно, без вмешательства какой-либо другой инструкции из другого потока. Итак, введите блокировку (ОНА же критическая секция), которая гарантирует, что эти две инструкции выполняются упорядоченно. Порядок функций был бы примерно таким:

 EnterCriticalSection
SetConsoleCursorPosition
SetConsoleTextAttribute
WriteConsole
LeaveCriticalSection
  

Критическая секция является общей для всех потоков.

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

Более сложным решением было бы использовать простое решение, но иметь два экранных буфера (с CreateConsoleScreenBuffer и SetConsoleActiveScreenBuffer) и постоянно переключать их для достижения мгновенного рисования.