Остановка потоков из общего кода.

artomka

Client
Регистрация
23.08.2018
Сообщения
157
Благодарностей
83
Баллы
28
Никак немогу разобраться с правильным завершением текущего потока из общего кода.

Пробовал через асинк метод выбрасывать ошибки/Environment.Exit() и в итоге закрывается сама зенка а не текущий поток.

Также пробовал через System.Threading.Thread.CurrentThread.Abort(); но это лишь закрывает текущий асинк метод.

Возможно кто сталкивался и может подсказать что не так?
 

artomka

Client
Регистрация
23.08.2018
Сообщения
157
Благодарностей
83
Баллы
28
П.С. при вызове System.Threading.Thread.CurrentThread.Abort(); из самой зенки летят забаные ошибки но поток не убивается.
 

Ilshakin

Client
Регистрация
14.02.2017
Сообщения
288
Благодарностей
118
Баллы
43
а если так:
C#:
ZennoPoster.StopTask(Guid.Parse(project.TaskId));
 

artomka

Client
Регистрация
23.08.2018
Сообщения
157
Благодарностей
83
Баллы
28

Phoenix78

Client
Регистрация
06.11.2018
Сообщения
6 120
Благодарностей
2 813
Баллы
113
что бы поток закрылся принудительно через определенный промежуток времени и вышел на BanEnd, надо поставить галочку и уставку времени.
70612


и оптимизировать шаблон так, что бы он хоть иногда выходил на межкубовые линии, так как эта настройка работает только в тех местах.
в циклах c# надо вставлять такие конструкции
C#:
// выход по внешнему требованию
if(((ZennoLab.InterfacesLibrary.ProjectModel.Collections.IContextExt)project.Context).IsInterrupted) throw new Exception("Внешнее прерывание");
if(Global.Variables.IsProjectMaker && !Global.Variables.IsDebugMode)  throw new Exception("Внешнее прерывание");
подробнее описывал тут https://zennolab.com/discussion/threads/lovim-zavisshie-instansy.72949/

а все эти махинации с жестким убиванием потока, это до добра не приведут, так как идентифицировать отдельный поток в зенке, ну очень проблематично.
 
  • Спасибо
Реакции: artomka

artomka

Client
Регистрация
23.08.2018
Сообщения
157
Благодарностей
83
Баллы
28
что бы поток закрылся принудительно через определенный промежуток времени и вышел на BanEnd, надо поставить галочку и уставку времени.
Посмотреть вложение 70612

и оптимизировать шаблон так, что бы он хоть иногда выходил на межкубовые линии, так как эта настройка работает только в тех местах.
в циклах c# надо вставлять такие конструкции
C#:
// выход по внешнему требованию
if(((ZennoLab.InterfacesLibrary.ProjectModel.Collections.IContextExt)project.Context).IsInterrupted) throw new Exception("Внешнее прерывание");
if(Global.Variables.IsProjectMaker && !Global.Variables.IsDebugMode)  throw new Exception("Внешнее прерывание");
подробнее описывал тут https://zennolab.com/discussion/threads/lovim-zavisshie-instansy.72949/

а все эти махинации с жестким убиванием потока, это до добра не приведут, так как идентифицировать отдельный поток в зенке, ну очень проблематично.
Cпасибо за статью но воспользовался решением от Verbin.

thread.Interrupt() и все работает как нужно ( но только в зп).

П.С. по поводу таймаута зенновского то он работает 50/50 с чем это связанно я не знаю.

Ну а по поводу оптимизации проекта + проверкам в кубиках c#, то это все конечно хорошо но тут нужно было решение без переписывания 100 кубиков :D поэтому просто асинк таймер + жесткий стоп через метод указанный выше работают на ура.
 
  • Спасибо
Реакции: VerBin

VerBin

Client
Регистрация
28.05.2016
Сообщения
554
Благодарностей
428
Баллы
63
Тестовый c# кубик в проекте:
// запускаем проверку в дочернем потоке с таймаутом в 5 секунд
project.Exit(5);

// имитируем долгое выполнение в течении 10 секунд
for (int counter = 0; counter < 10; counter++)
{
    project.SendInfoToLog($"Поток: {Thread.CurrentThread.ManagedThreadId} Счетчик: {counter}", true);
    Thread.Sleep(1000);
}
Класс в общем коде:
public static class ProjectExtensions
{
    /// <summary>
    /// Остановить выполнение проекта по таймауту
    /// </summary>
    public static void Exit(this IZennoPosterProjectModel project, int timeoutInSeconds)
    {
        Thread thread = Thread.CurrentThread;
        System.Threading.Tasks.Task.Run(() =>
                                        {
                                            int counter = 0;

                                            while (true)
                                            {
                                                Thread.Sleep(1000);
                                                counter++;

                                                if (counter > timeoutInSeconds)
                                                {
                                                    project.SendInfoToLog($"Останавливаем поток по таймауту: {thread.ManagedThreadId}", true);
                                                    thread.Interrupt();

                                                    break;
                                                }
                                            }
                                        });
    }
}
 

Вложения

  • 12,9 КБ Просмотры: 30
  • Спасибо
Реакции: SHILY, Gor и artomka

Gor

Client
Регистрация
30.09.2016
Сообщения
244
Благодарностей
27
Баллы
28
// запускаем проверку в дочернем потоке с таймаутом в 5 секунд
project.Exit(5);
Нет, ну круто, конечно, чё... И как раз ответ на вопрос, который меня долго мучил... Но только если основной поток завершаентся раньше фонового, вся зенка крэшится!
Вопрос в том, почему и как этого избежать? Крэшится на строке thread.Interrupt(); в общем коде.
 

Вложения

  • 12,5 КБ Просмотры: 19
Последнее редактирование:

Phoenix78

Client
Регистрация
06.11.2018
Сообщения
6 120
Благодарностей
2 813
Баллы
113
ну что , доработаем этот костыль ? :-)

а что если внутрь параллельного вставить этот код ?
C#:
// выход по внешнему требованию
if(((ZennoLab.InterfacesLibrary.ProjectModel.Collections.IContextExt)project.Context).IsInterrupted) {thread.Interrupt(); break;}
if(Global.Variables.IsProjectMaker && !Global.Variables.IsDebugMode)  {thread.Interrupt(); break;}
пробовать не буду, сами пробуйте :-)
 

artomka

Client
Регистрация
23.08.2018
Сообщения
157
Благодарностей
83
Баллы
28
Нет, ну круто, конечно, чё... И как раз ответ на вопрос, который меня долго мучил... Но только если основной поток завершаентся раньше фонового, вся зенка крэшится!
Вопрос в том, почему и как этого избежать? Крэшится на строке thread.Interrupt(); в общем коде.
Ответил вам в личку но напишу еще раз тут.

Зенка не крашится а кладет поток, в ПМ он всего 1 и соотвестенно кладется весь ПМ. В зп таких проблем не будет.

ну что , доработаем этот костыль ? :-)

а что если внутрь параллельного вставить этот код ?
C#:
// выход по внешнему требованию
if(((ZennoLab.InterfacesLibrary.ProjectModel.Collections.IContextExt)project.Context).IsInterrupted) {thread.Interrupt(); break;}
if(Global.Variables.IsProjectMaker && !Global.Variables.IsDebugMode)  {thread.Interrupt(); break;}
пробовать не буду, сами пробуйте :-)
Чуть позже опробую когда закончу дела.
 

VerBin

Client
Регистрация
28.05.2016
Сообщения
554
Благодарностей
428
Баллы
63
Нет, ну круто, конечно, чё... И как раз ответ на вопрос, который меня долго мучил... Но только если основной поток завершаентся раньше фонового, вся зенка крэшится!
Вопрос в том, почему и как этого избежать? Крэшится на строке thread.Interrupt(); в общем коде.
Да действительно, если фоновый поток работает дольше основного, то зенка вылетает.
Сейчас выложу обновленное решение..
 

VerBin

Client
Регистрация
28.05.2016
Сообщения
554
Благодарностей
428
Баллы
63
Переработал и протестировал код.
Обновленное решение работает стабильно в ProjectMaker и ZennoPoster.

В общий код добавляем класс:
C#:
public class Timeout
{
    private IZennoPosterProjectModel _project = null;
    private int _timeoutInSeconds = 0;
    private CancellationTokenSource _cancellationTokenSource = null;

    public Timeout(IZennoPosterProjectModel project, int timeoutInSeconds)
    {
        _project = project;
        _timeoutInSeconds = timeoutInSeconds;
        _cancellationTokenSource = new CancellationTokenSource();

    }

    public void Start()
    {
        var thread = Thread.CurrentThread;
        System.Threading.Tasks.Task.Factory.StartNew(() =>
                                                     {
                                                         _project.SendInfoToLog($"Запускаем фоновый поток: {Thread.CurrentThread.ManagedThreadId}", true);
                                                         var endTime = DateTime.Now.AddSeconds(Convert.ToDouble(_timeoutInSeconds));
                                                         while (true)
                                                         {
                                                             Thread.Sleep(100);

                                                             if (_cancellationTokenSource.Token.IsCancellationRequested)
                                                             {
                                                                 _project.SendInfoToLog($"Останавливаем фоновый поток: {Thread.CurrentThread.ManagedThreadId}", true);
                                                                 break;
                                                             }

                                                             var currentTime = DateTime.Now;
                                                             if (currentTime >= endTime)
                                                             {
                                                                 _project.SendInfoToLog($"Останавливаем основной поток: {thread.ManagedThreadId}", true);
                                                                 _project.SendInfoToLog($"Останавливаем фоновый поток: {Thread.CurrentThread.ManagedThreadId}", true);
                                                                 thread.Interrupt();
                                                                 break;
                                                             }
                                                         }
                                                     }, _cancellationTokenSource.Token);
    }

    public void Stop()
    {
        _cancellationTokenSource.Cancel();
        Thread.Sleep(1000);
    }
}
Первый кубик в проекте для запуска фонового потока с проверкой по таймауту:
C#:
// запускаем проверку в фоновном потоке с таймаутом в 5 секунд
project.Context["timeout"] = new Timeout(project, 5);
(project.Context["timeout"] as Timeout).Start();
Второй кубик в проекте для имитации выполнения разных задач:
C#:
// имитируем долгое выполнение в течении 10 секунд
for (int counter = 0; counter < 10; counter++)
{
    project.SendInfoToLog($"Поток: {Thread.CurrentThread.ManagedThreadId} Счетчик: {counter}", true);
    Thread.Sleep(1000);
}
Кубик завершения проекта по Bad End и Good End для завершения фонового потока:
C#:
(project.Context["timeout"] as Timeout).Stop();
Прикрепил к сообщению тестовый проект, работает на версии 7.2 и выше.

Если вам пригодилось решение, достаточно нажать на кнопку Спасибо под сообщением.
 

Вложения

  • 16,7 КБ Просмотры: 22
Последнее редактирование:

VerBin

Client
Регистрация
28.05.2016
Сообщения
554
Благодарностей
428
Баллы
63
ну все :-)
прям рабочий механизм для запуска многопоточных процедур в зенке. щас все начнут запускать распараллеливание :-)
Я бы назвал это костылем.
Но все-же, кому-то это решение крайне необходимо.
 
Последнее редактирование:

Phoenix78

Client
Регистрация
06.11.2018
Сообщения
6 120
Благодарностей
2 813
Баллы
113
позанудствую :-)
после _cancellationTokenSource.Cancel(); я бы все же поставил небольшую паузу. хоть в параллельном потоке и стоит достаточно маленькая пауза в 100 мс. но разрыв все равно есть.
 
  • Спасибо
Реакции: VerBin

VerBin

Client
Регистрация
28.05.2016
Сообщения
554
Благодарностей
428
Баллы
63
позанудствую :-)
после _cancellationTokenSource.Cancel(); я бы все же поставил небольшую паузу. хоть в параллельном потоке и стоит достаточно маленькая пауза в 100 мс. но разрыв все равно есть.
Логично. Обновил код в сообщении.
 
Последнее редактирование:

Phoenix78

Client
Регистрация
06.11.2018
Сообщения
6 120
Благодарностей
2 813
Баллы
113
я это.... не то что придираюсь .... :-)
не пойму что вот это за условие такое на скрине, у меня оно всегда в false и выполнение кода идет бесконечно.

70710
 

VerBin

Client
Регистрация
28.05.2016
Сообщения
554
Благодарностей
428
Баллы
63
я это.... не то что придираюсь .... :-)
не пойму что вот это за условие такое на скрине, у меня оно всегда в false и выполнение кода идет бесконечно.

Посмотреть вложение 70710
Это проверка жив ли основной поток. Можешь удалить. Вставил на всякий случай.
Обновил код, перезалил проект.
 
Последнее редактирование:
  • Спасибо
Реакции: artomka

Gor

Client
Регистрация
30.09.2016
Сообщения
244
Благодарностей
27
Баллы
28
Переработал и протестировал код.
Обновленное решение работает стабильно в ProjectMaker и ZennoPoster.
Спасибо большое! Очень ценная информация. Я пока до совсем многопоточного программирования не вырос, так что придется сделать левел ап чтобы понять полностью. Но код рабочий, все супер! Вы таки мастер! )
 

Gor

Client
Регистрация
30.09.2016
Сообщения
244
Благодарностей
27
Баллы
28
Чуть попроще сделал с моей точки зрения аматора в многопоточности. Один экшен, один снипет. В общую коллекцию.
C#:
Thread thread = Thread.CurrentThread;
Task.Factory.StartNew(()=>
{
    while (thread.IsAlive)
    {
        project.SendInfoToLog("Фоновый поток", true);
        
        // Какие-то действия, эмулируем ожиданием
        project.SendInfoToLog("Фоновый поток - Первое ожидание. Состояние основного потока: " + thread.IsAlive.ToString(), true);
        Thread.Sleep(1000);
        project.SendInfoToLog("Фоновый поток - Второе ожидание. Состояние основного потока: " + thread.IsAlive.ToString(), true);
        Thread.Sleep(1000);
        
        project.SendInfoToLog("Фоновый поток - прерывание основного потока. Состояние основного потока: " + thread.IsAlive.ToString(), true);
        thread.Interrupt();
    }
    project.SendInfoToLog("Фоновый поток конец", true);
}
);
project.SendInfoToLog("Основной поток", true);

// Какие-то действия, эмулируем ожиданием
Thread.Sleep(10000);

project.SendInfoToLog("Основной поток конец", true);
 

Вложения

Phoenix78

Client
Регистрация
06.11.2018
Сообщения
6 120
Благодарностей
2 813
Баллы
113
Чуть попроще сделал с моей точки зрения аматора в многопоточности. Один экшен, один снипет. В общую коллекцию.
у тебя весь шаблон из одного сниппета состоит ?
 

Gor

Client
Регистрация
30.09.2016
Сообщения
244
Благодарностей
27
Баллы
28
у тебя весь шаблон из одного сниппета состоит ?
Да, но он не вполне корректно работает, как оказалось. Немного не та цель выполняется.
Если действия описаны и выполняются в пределах этого одного кубика - все норм. Фоновый поток мониторит основной поток (жив ли он), ожидает немного (вместо ожидания можно прописать свои действия) прерывает его и завершается сам. Но как только действие шаблона переходит к выполнению других кубиков, прерывание основного потока не происходит. Вероятно потому, что при выполнении каждого последующего кубика создаётся новы поток. По крайней мере идентификатор потока (его номер) другой.
 

Phoenix78

Client
Регистрация
06.11.2018
Сообщения
6 120
Благодарностей
2 813
Баллы
113
походу ты запутался в шаблонах, потоках и иже там :-)
я тебя спрашивал про твой шаблон. ты говоришь что весь шаблон состоит из одного кубика, и тут же говоришь что на следующем кубике код не работает.
конечно он не работает, ты же вызываешь thread.Interrupt(); в том же кубике в котором и вызываешь запуск кода.
вот здесь https://zennolab.com/discussion/threads/ostanovka-potokov-iz-obschego-koda.87667/post-589411
все разложено как и что вызывать, как завершать. а твой вариант не имеет смысла совсем. ну может и имеет для контроля конкретного одного кубика, но ни как не относится к теме топика, а именно контролю всего проекта.
 

Кто просматривает тему: (Всего: 1, Пользователи: 0, Гости: 1)