Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 5.00/21: Рейтинг темы: голосов - 21, средняя оценка - 5.00
 Аватар для CyberPaladin
18 / 16 / 8
Регистрация: 27.05.2017
Сообщений: 118

Как перезапустить поток после нажатия клавиши без исключений

17.12.2018, 20:32. Показов 4513. Ответов 8
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Добрый вечер, коллеги.
Я пишу многопоточную консольную змейку для учебного проекта и все отлично работает за исключением концовки программы.

Так вот ПРОБЛЕМА: когда я пытаюсь нажать клавишу для того чтобы повторить игру(т.е. по сути запустить весь игровой цикл заново то программа падает и выдает необработанное исключение типа "System.Threading.ThreadStateExcepti on" вылетает на второй итерации игрового цикла.
Я хочу только запустить поток заново или завершить поток и затем запустить снова, но не знаю что делать(пробовал метод Abort() - не работает все равно(((

Задумка такая, что из метода Main, изначально мы запускаем игровой метод SetArcadeMode() в котором собственно запускаем поток игры в фоновом режиме StreamArcadeGame() который отвечает за игровую логику(ВСЕ ЭТО РАБОТАЕТ!).
Далее когда мы проигрываем(флаг GAMEOVER == false) то прерываем метод потока и возвращаемся в метод SetArcadeMode() где показываем концовку игры и предлагаем сыграть еще раз или завершить по нажатию Esc или N( за это отвечает данный метод):

C#
1
2
3
4
5
6
7
8
9
10
11
12
static bool RepeatGame(bool isGameover)
        {
            if (isGameover)
            {
                keyInfo = Console.ReadKey(true);
                if (keyInfo.Key == ConsoleKey.Escape || keyInfo.Key == ConsoleKey.N) //нормально завершаем игру и саму программу
                return false;
            }
            //backgroundArcade.Join(); //вот тут ошибка
            Console.Clear();
            return true; //если другая клавиша возвращаемся
        }
Вот весь код:
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
private static Thread backgroundArcade  = new Thread(StreamArcadeGame); //поток игры
/*
   Другой код
*/
static void Main(string[] args)
        {
            /*
              Другой код
            */
            Console.CursorVisible = false;
            Graphics.ShowLogo(); //стартовый экран
 
            while (true) //обработчик нажатия клавиш
                keyInfo = Console.ReadKey(true);
 
                if (keyInfo.Key == ConsoleKey.Escape)
                    Environment.Exit(0); //выход
     
                if (keyInfo.Key == ConsoleKey.D2)
                {
                    for (int i = 0; i < 6; i++)
                    {
                        Graphics.Blink(2); // просто графический эффект
                    }
                    gameMode = Mode.Arcade;
                    break;
                }
                    for (int i = 0; i < 3; i++) //если нажали что-то неправильно
                    {
                        Graphics.Blink(0);
                    }
            }
 
            Console.Clear();
 
            if (gameMode == Mode.Arcade)
                SetArcadeMode(); //исполняем игровой цикл в методе(код ниже)
        }
/*
  Другой код
*/
 
static void StreamArcadeGame() //метод который выполняется потоком в фоновом режиме
        {
            while (!GAMEOVER)
            {
                if (PAUSE)
                {
                    Console.BackgroundColor = ConsoleColor.Blue;
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.SetCursorPosition(30, 12);
                    Console.Write("GAME PAUSED");
 
                    keyInfo = Console.ReadKey(true);
                    while (keyInfo.Key != ConsoleKey.P)
                    {
                        keyInfo = Console.ReadKey(true); //игровая пауза
                    }
 
                    PAUSE = false;
                    Console.BackgroundColor = ConsoleColor.Black;
                    Console.ForegroundColor = ConsoleColor.White;
                    Console.SetCursorPosition(30, 12);
                    Console.Write("            ");
                }
 
                for (int i = 1; i < snake.Count; i++)
                {
                    if (head.X == snake[i].X && head.Y == snake[i].Y) //если змейка столкнулась со стенкой или своим телом
                        GAMEOVER = true; //то конец
                }
                if (GAMEOVER) //прерываем игру
                    break; //выходим из цикла
                if (FOODEXIST == false)
                    CreateFood();
                MoveSnake();
                if (head.X == meal.X && head.Y == meal.Y)
                {
                    IEAT = true;
                    score += Convert.ToInt64((Convert.ToDouble(snake.Count) / 3) * 10); //считаем очки
                    Console.SetCursorPosition(63, 0);
                    Console.Write(score); //выводим очки
                }
                if (IEAT)
                {
                    EatFood();
                }
                Thread.Sleep(speed); //тормозим поток
            }
        }
/*
   Другой код
*/
static void SetArcadeMode() 
        {
            do //собственно игровой цикл
            {
                InitiateSnake(); //прорисовка змейки
                DrawField(); //прорисовка поля
 
                Console.SetCursorPosition(3, 0); //установка статистической инфы 1
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.Write("LEVEL 1");
                Console.ForegroundColor = ConsoleColor.White;
 
                Console.SetCursorPosition(56, 0); //установка статистической инфы 2
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.Write("SCORE: ");
                Console.ForegroundColor = ConsoleColor.White;
                Console.SetCursorPosition(63, 0);
                Console.Write(score);
                 //ВОТ СДЕСЬ ВЫЛЕТАЕТ ИСКЛЮЧЕНИЕ ПРИ 2 ИТЕРАЦИИ ЦИКЛА!!
                backgroundArcade.Start(); //запускаем фоновый поток
                backgroundArcade.IsBackground = true; //делаем его фоновым
 
                ControlArcadeGame(); //позволяет пользователю управлять игрой
 
                Console.Clear();
                Graphics.ShowEnd(score, SnakeLength()); //финиш
 
            } while (RepeatGame(GAMEOVER)); //повторяем если не проиграли
            Environment.Exit(0);
        }
0
Лучшие ответы (1)
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
17.12.2018, 20:32
Ответы с готовыми решениями:

Продолжить выполнение программы только после нажатия определенной клавиши.
Здравствуйте. Столкнулся с проблемой, необходимо приостановить ход программы : txtBoxNameAfter.Text = workingMP3.id3Title; ...

Вывод на экран нескольких разных строк после нажатия рандомной клавиши
Как сделать так, чтобы каждая отдельная строка текста выводилась на экран после очередного нажатия любой клавиши пользователем? То есть...

После нажатия клавиши Enter или нажатия на Button
Помогите пожалуйста.:-[ Задача: После нажатия клавиши Enter или нажатия на Button поле курсора переходит в соответствующий компонент...

8
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10427 / 5157 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
17.12.2018, 20:43
Цитата Сообщение от CyberPaladin Посмотреть сообщение
Как перезапустить поток после нажатия клавиши без исключений
Завершенный поток не может быть перезапущен. Нужно создавать новый экземпляр потока и запускать его.
Кроме того, вам здесь поток вообще не нужен. Игровой цикл вполне можно запускать в основном потоке.
0
 Аватар для CyberPaladin
18 / 16 / 8
Регистрация: 27.05.2017
Сообщений: 118
17.12.2018, 20:59  [ТС]
Storm23, да, в принципе возможно все сделать и без потоков, но таковы требования - приложение должно быть многопоточным!
К тому же я показал лишь часть кода, а на самом деле в моей программе аж 3 потока(не связанных друг с другом) и если переделать без потоков то это много времени займет.
Пожалуйста, подскажите какой метод использовать. Я пробовал Abort() и Join() и не совсем понятно как их правильно применить?? Это мое первое многопоточное приложение и очень запутывает то что потоки во время выполнения программы в разных состояниях еще могут пребывать
0
 Аватар для PoPsa85
814 / 561 / 240
Регистрация: 01.07.2018
Сообщений: 1,818
17.12.2018, 21:11
CyberPaladin, А почему не Task ?
0
 Аватар для Nalik
176 / 124 / 49
Регистрация: 30.11.2012
Сообщений: 1,330
17.12.2018, 21:19
Лучший ответ Сообщение было отмечено CyberPaladin как решение

Решение

Перед запуском создавайте новый экземпляр потока.
C#
1
2
if (backgroundArcade.IsAlive) { backgroundArcade.Abort(); }
backgroundArcade = new Thread(StreamArcadeGame); //поток игры
1
 Аватар для CyberPaladin
18 / 16 / 8
Регистрация: 27.05.2017
Сообщений: 118
18.12.2018, 02:26  [ТС]
Nalik, ваше решение действительно помогло избавится от исключения, но теперь проблема в том что когда после проверки статуса конца игры я нажимаю клавишу отличительную от Esc то консоль сначала прорисовывает всю игру а затем сразу же её завершает(т.е. поток исполняется втупую до конца цикла)

С начала я создавал новый экземпляр потока в проверяющем методе:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static bool RepeatGame(bool isGameover)
        {
            if (isGameover)
            {
                keyInfo = Console.ReadKey(true);
                if (keyInfo.Key == ConsoleKey.Escape || keyInfo.Key == ConsoleKey.N)
                    return false;
                else
                {
                            if (backgroundArcade.IsAlive)
                            {
                                backgroundArcade.Abort();
                                Console.Clear();
                            }
                            backgroundArcade = new Thread(StreamArcadeGame);
                            break;
                } 
            }
            Console.Clear();
            return true;
        }
Затем просто засунул в начало игрового цикла...

Собственно говоря эффект тот же самый - поток летит без остановки.

Добавлено через 5 минут
PoPsa85, по правде говоря, я не углублялся в использование Task-ов, но судя по методам и свойствам этого класса, вся эта тема похожа на Thread. Значит логично предположить что и изменений никаких за собой это не повлечет.
В общем я особо разницы не уловил между Thread и Task
0
 Аватар для Nalik
176 / 124 / 49
Регистрация: 30.11.2012
Сообщений: 1,330
18.12.2018, 08:43
Где-то у вас логическая ошибка. Сможете скинуть проект?
0
 Аватар для CyberPaladin
18 / 16 / 8
Регистрация: 27.05.2017
Сообщений: 118
18.12.2018, 20:38  [ТС]
Nalik, я все же решил последнюю проблему!! И да, вы правы это была логическая.
Я просто забыл после того как создал новый экземпляр потока переставить глобальный флаг GAMEOVER в значение false. Вот и получалось что поток перезапускал игру со статусом GAMEOVER изначально - т.е. рисовал уровень, змейку а в метод управления игнорировался т.к. статус был неправильный.
И вот правильный метод перезапуска игрового цикла с использованием потока:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static bool RepeatGame(bool isGameover)
        {
            if (isGameover)
            {
                keyInfo = Console.ReadKey(true);
                if (keyInfo.Key == ConsoleKey.Escape || keyInfo.Key == ConsoleKey.N)
                    return false;
                else
                {
                            if (backgroundArcade.IsAlive)
                            {
                                backgroundArcade.Abort();
                                Console.Clear();
                            }
                            backgroundArcade = new Thread(StreamArcadeGame); //создаем управляющий поток
                            GAMEOVER = false; //нормализируем игровой статус 
                            break;
                } 
            }
            Console.Clear();
            return true;
        }
Благодарю за то что указали правильное решение!
0
 Аватар для Nalik
176 / 124 / 49
Регистрация: 30.11.2012
Сообщений: 1,330
18.12.2018, 20:49
Супер
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
18.12.2018, 20:49
Помогаю со студенческими работами здесь

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

Побуквенный вывод слова через цикл + ожидания нажатия клавиши (после нажатия очистка и установка курсора на )
строку) Masm32 (com -программа) вообщем столкнулся с проблемой. к примеру ввожу ' LSD49 $' выводится через цикл loop (в ECX -...

Как сделать повтор действия после нажатия любой клавиши
Задача : Разработать программу, в которой описать переменные для создания соответствующей динамической структуры. Рекомендуется в...

Как отменить дальнейшую обработку нажатия клавиши после перехвата?
Хочу по нажатию Ctrl+P в форме вместо печати формы выводить на печать отчет. Нажатие Ctrl+P перехватила, код: Private Sub...

Как приостановить и перезапустить поток
Есть следующее задание: Написать приложение, содержащее не менее двух тредов. Каждый из этих тредов должен искать файлы: - с...


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

Или воспользуйтесь поиском по форуму:
9
Ответ Создать тему
Новые блоги и статьи
Как дизайн сайта влияет на конверсию: 7 решений, которые реально повышают заявки
Neotwalker 08.03.2026
Многие до сих пор воспринимают дизайн сайта как “красивую оболочку”. На практике всё иначе: дизайн напрямую влияет на то, оставит человек заявку или уйдёт через несколько секунд. Даже если у вас. . .
Модульная разработка через nuget packages
DevAlt 07.03.2026
Сложившийся в . Net-среде способ разработки чаще всего предполагает монорепозиторий в котором находятся все исходники. При создании нового решения, мы просто добавляем нужные проекты и имеем. . .
Модульный подход на примере F#
DevAlt 06.03.2026
В блоге дяди Боба наткнулся на такое определение: В этой книге («Подход, основанный на вариантах использования») Ивар утверждает, что архитектура программного обеспечения — это структуры,. . .
Управление камерой с помощью скрипта OrbitControls.js на Three.js: Вращение, зум и панорамирование
8Observer8 05.03.2026
Содержание блога Финальная демка в браузере работает на Desktop и мобильных браузерах. Итоговый код: orbit-controls-threejs-js. zip. Сканируйте QR-код на мобильном. Вращайте камеру одним пальцем,. . .
SDL3 для Web (WebAssembly): Синхронизация спрайтов SDL3 и тел Box2D
8Observer8 04.03.2026
Содержание блога Финальная демка в браузере. Итоговый код: finish-sync-physics-sprites-sdl3-c. zip На первой гифке отладочные линии отключены, а на второй включены:. . .
SDL3 для Web (WebAssembly): Идентификация объектов на Box2D v3 - использование userData и событий коллизий
8Observer8 02.03.2026
Содержание блога Финальная демка в браузере. Итоговый код: finish-collision-events-sdl3-c. zip Сканируйте QR-код на мобильном и вы увидите, что появится джойстик для управления главным героем. . . .
Реалии
Hrethgir 01.03.2026
Нет, я не закончил до сих пор симулятор. Эта задача сложнее. Не получилось уйти в плавсостав, но оно и к лучшему, возможно. Точнее получалось - но сварщиком в палубную команду, а это значит, в моём. . .
Ритм жизни
kumehtar 27.02.2026
Иногда приходится жить в ритме, где дел становится всё больше, а вовлечения в происходящее — всё меньше. Плотный график не даёт вниманию закрепиться ни на одном событии. Утро начинается с быстрых,. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru