powered by simpleCommunicator - 2.0.51     © 2025 Programmizd 02
Форумы / WPF, Silverlight [игнор отключен] [закрыт для гостей] / Отмена задачи на последней итерации вешает приложение
3 сообщений из 3, страница 1 из 1
Отмена задачи на последней итерации вешает приложение
    #38800351
monstrilla
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Есть приложение, в нем запущена задача в фоне, в задаче цикл. Если задачу отменить на любой итерации цикла кроме последней - все ок. Если отменить на последней итерации - приложение зависнет на строке _myTask.Wait(); . Как я полагаю, это происходит из-за того, что сигнал отмены прилетает уже после того как в фоновом потоке будут пройдены строки

Код: c#
1.
2.
if (_token.IsCancellationRequested)
    _token.ThrowIfCancellationRequested();



и тут что-то видимо идет не так. Прилагаю код, чтоб было понятнее, что происходит, но данный код, к сожалению, НЕ ВОСПРОИЗВОДИТ баг. По непонятным причинам, не воспроизводит и все, хотя я в точности (как мне кажется) сэмулировал то, что происходит в реальном проекте. Может быть есть какие-то соображения, что это может быть?

.Xaml

Код: xml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
<Grid>
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <TextBox Grid.Row="0" 
                 Text="{Binding IterationCount}"/>
        <TextBlock Grid.Row="1" Text="{Binding AnnounceString}"/>
        <Button Grid.Row="2" 
                Click="RunButtonOnClick"
                Content="Run task"></Button>
        <Button Grid.Row="3"
                Content="Cancel task"
                Click="CancelButtonOnClick"></Button>
</Grid>



.cs

Код: 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.
        private Task   _myTask;
        private String _announceString;
        private CancellationTokenSource _tokenSource;

        private CancellationToken _token    ;

        public Int32 IterationCount { get; set; }

        public String AnnounceString    
        {
            get { return _announceString; }
            set
            {
                _announceString = value;
                OnPropertyChanged("AnnounceString");
            }
        }

        public Window1()
        {
            InitializeComponent();
            DataContext = this;
            IterationCount = 5;

            
        }

        private void RunButtonOnClick(Object sender, RoutedEventArgs e)
        {
            _tokenSource = new CancellationTokenSource();
            _token = _tokenSource.Token;

            _myTask = Task.Factory.StartNew(() =>
            {
                for (Int32 i = 0; i < IterationCount; i++)
                {
                    if (_token.IsCancellationRequested)
                        _token.ThrowIfCancellationRequested();

                    AnnounceString = String.Format("Iteration {0} is bloked", i + 1);

                    Thread.SpinWait( 10 * 100000000);
                }
                AnnounceString = "TaskCompleted";
            }, _token, TaskCreationOptions.None, TaskScheduler.Default);
        }

        private void CancelButtonOnClick(Object sender, RoutedEventArgs e)
        {
            try
            {
                _tokenSource.Cancel();
                _myTask.Wait();
            }
            catch (Exception)
            {
                _tokenSource.Dispose();
                AnnounceString = "TaskCancelled";    
            }
        }

        #region INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged(String propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion
...
Рейтинг: 0 / 0
Отмена задачи на последней итерации вешает приложение
    #38800463
monstrilla
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Нашел как стопроцентно эмулировать баг. Добавляем в приложение таймер.

Код: c#
1.
using Timer = System.Windows.Forms.Timer;



Код: c#
1.
private Timer _myTimer;



В конструторе пишем

Код: c#
1.
2.
_myTimer = new Timer { Interval = 5 * 60000 };
_myTimer.Start();



Самое главное в методе.

Код: 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.
private void RunButtonOnClick(Object sender, RoutedEventArgs e)
        {
            _tokenSource = new CancellationTokenSource();
            _token = _tokenSource.Token;

            _myTask = Task.Factory.StartNew(() =>
            {
                for (Int32 i = 0; i < IterationCount; i++)
                {
                    if (_token.IsCancellationRequested)
                        _token.ThrowIfCancellationRequested();

                    AnnounceString = String.Format("Iteration {0} is bloked", i + 1);

                    Thread.SpinWait( 10 * 100000000);
                }
                AnnounceString = "TaskCompleted";

                // Вот так работает
                _myTimer.Interval = 6 * 60000;
                _myTimer.Start();

                // А если вот так, то не работает
                //Application.Current.Dispatcher.Invoke(new Action(RerunTimer), null);

            }, _token, TaskCreationOptions.None, TaskScheduler.Default);
        }



Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
        /// <summary>
        /// Перезапуск таймера в потоке UI.
        /// </summary>
        private void RerunTimer()
        {
            _myTimer.Interval = 6 * 60000;
            _myTimer.Start();
        }



В принципе, замена Application.Current.Dispatcher.Invoke на Application.Current.Dispatcher.BeginInvoke лечит ситуацию. Похоже, соль в следующем: _myTask.Wait(); вешает основной поток, ждет, когда задача завершится. Если итерация не последняя, то на следующей итерации сработают строки

Код: c#
1.
2.
if (_token.IsCancellationRequested)
    _token.ThrowIfCancellationRequested();



Будет исключение и выход из задачи, до строки с таймером код не дойдет. А на последней итерации отмены задачи не произойдет, она будет завершаться в штатном режиме, дойдет до строки с таймером и там будет пытаться у ожидающего потока вызвать метод. Дедлок.
...
Рейтинг: 0 / 0
Отмена задачи на последней итерации вешает приложение
    #38801577
Roman Mejtes
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
monstrilla,

чтобы завершить поток, нужно завершить все потоки которые вы в нём вызывали.
...
Рейтинг: 0 / 0
3 сообщений из 3, страница 1 из 1
Форумы / WPF, Silverlight [игнор отключен] [закрыт для гостей] / Отмена задачи на последней итерации вешает приложение
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]