powered by simpleCommunicator - 2.0.53     © 2025 Programmizd 02
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / BackgroundWorker - не завершается
21 сообщений из 21, страница 1 из 1
BackgroundWorker - не завершается
    #39412572
13th
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Добрый день. Прошу помочь с BackgroundWorker, чё-то у меня в него не получается.

Есть UI с несколькими элементами. При нажатии на каждый - запускается фоновая задача. Информацию о том, какой элемент нажат - помещаю в свойство selectedItem. Причём, если задача запущена, предыдущую фоновую задачу надо завершить, и запустить заново с новыми параметрами.

Если я дожидаюсь окончания выполнения фоновой задачи - всё работает как ожидается. Но если пытаюсь прерывать - почему-то не вызывается RunWorkerCompleted и worker постоянно висит в состоянии Busy, причём поток останавливается. Подскажите, что не так?

Код: 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.
	public class tViewModel : INotifyPropertyChanged
	{
		public tViewModel()
		{
			selectedItem = "";

			bgWorker.DoWork += bg_DoWork;
			bgWorker.RunWorkerCompleted += bg_RunWorkerCompleted;
		}

		private BackgroundWorker bgWorker = new BackgroundWorker { WorkerReportsProgress = false, WorkerSupportsCancellation = true };

		private void bg_DoWork(object sender, DoWorkEventArgs e)
		{
			string workName = "Unknown";
			int n = 10;
			do
			{
				if (e.Argument is string)
					workName = e.Argument as string;
				Debug.WriteLine("\tbg work '" + workName + "'");
				if (bgWorker.CancellationPending)
				{
					e.Cancel = true;
					return;
				}
				Thread.Sleep(300);
				n--;
			}
			while (n > 0);
			e.Result = 1;
		}

		private void bg_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
			if (e.Cancelled)
				Debug.WriteLine("bg_RunWorkerCompleted: bg work canceled");
			else
				Debug.WriteLine("bg_RunWorkerCompleted: bg work ended with result " + e.Result.ToString());
		}

		private string _selectedItem = "";
		public string selectedItem
		{
			get
			{
				return _selectedItem;
			}
			set
			{
				lock (_selectedItem)
				{
					if (_selectedItem == value)
						return;

					_selectedItem = value;
				}

				OnPropertyChanged("selectedItem");
				if (_selectedItem == "")
					return;

				Debug.WriteLine("set_selectedItem: Try to start bg work");

				if (bgWorker.IsBusy)
				{
					Debug.WriteLine("bgWorker is busy, cancel async");
					bgWorker.CancelAsync();
				}

				Debug.WriteLine("set_selectedItem: Startint bg work");
				while (bgWorker.IsBusy) Thread.Sleep(100);
				bgWorker.RunWorkerAsync(_selectedItem);
				Debug.WriteLine("set_selectedItem: Bg work started");
			}
		}

		#region INotifyPropertyChanged

		public event PropertyChangedEventHandler PropertyChanged;

		private void OnPropertyChanged(string propertyName)
		{
			if (PropertyChanged != null)
			{
				PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
			}
		}

		#endregion INotifyPropertyChanged

	}



При этом имею вот такой Output:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
The thread 'vshost.NotifyLoad' (0x1f84) has exited with code 0 (0x0).
The thread '<No Name>' (0x788) has exited with code 0 (0x0).
The thread '<No Name>' (0x1838) has exited with code 0 (0x0).
The thread 'vshost.LoadReference' (0x1ca4) has exited with code 0 (0x0).
Step into: Stepping over method without symbols 'FolderStat.App.App'
set_selectedFolder: Try to start bg work
set_selectedFolder: Startint bg work
set_selectedFolder: Bg work started
	bg work '1st task'
	bg work '1st task'
	bg work '1st task'
set_selectedFolder: Try to start bg work
bgWorker is busy, cancel async
set_selectedFolder: Startint bg work
	bg work '1st task'
------------------------- ВИСИМ секунд 15
The thread '<No Name>' (0xd0) has exited with code 0 (0x0).
The thread '<No Name>' (0x1a28) has exited with code 0 (0x0).

Главный поток висит на строке while (bgWorker.IsBusy) Thread.Sleep(100); вечно.
Если её убрать, на строке bgWorker.RunWorkerAsync(_selectedItem); получаем InvalidOperationException: This BackgroundWorker is currently busy and cannot run multiple tasks concurrently.
...
Рейтинг: 0 / 0
BackgroundWorker - не завершается
    #39412644
13th
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Ступил, теперь разобрался. Спасибо за внимание.
...
Рейтинг: 0 / 0
BackgroundWorker - не завершается
    #39412646
ВМоисеев
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
>13th, сегодня, 15:57 [20254666]
>...Прошу помочь с BackgroundWorker...

Посмотри здесь .

С уважением,
Владимир
...
Рейтинг: 0 / 0
BackgroundWorker - не завершается
    #39412701
13th
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
ВМоисеев>13th, сегодня, 15:57 [20254666]
>...Прошу помочь с BackgroundWorker...

Посмотри здесь .

С уважением,
Владимир

Надо же, какой хамский ответ...
...
Рейтинг: 0 / 0
BackgroundWorker - не завершается
    #39412766
ВМоисеев
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
>13th, сегодня, 18:00 [20255089]
>Надо же, какой хамский ответ...

Не понял.
В разделе Примеры похоже разобрана ваша задача.
...
Рейтинг: 0 / 0
BackgroundWorker - не завершается
    #39413154
13th
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Сначала не дал себе труда прочесть первый пост, что бы понять, в чём проблема, и даёшь ссылку на статью MSDN по BackgroundWorker: типа мальчик, читай документацию. Хотя по моему примеру видно, что документация прочтена.
Ну и потом выясняется, что и сам статью не читал, т.к. там тоже нет решения моей проблемы.

Там везде написано так:
Код: c#
1.
2.
3.
4.
            if (backgroundWorker1.IsBusy != true)
            {
                backgroundWorker1.RunWorkerAsync();
            }



а мне-то как раз надо запустить, если backgroundWorker1.IsBusy равно true. Именно в MSDN этого и нет, хотя решение не сложное.
...
Рейтинг: 0 / 0
BackgroundWorker - не завершается
    #39413453
Pallaris
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
13th
Код: 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.
			set
			{
				lock (_selectedItem)
				{
					if (_selectedItem == value)
						return;

					_selectedItem = value;
				}

				OnPropertyChanged("selectedItem");
				if (_selectedItem == "")
					return;

				Debug.WriteLine("set_selectedItem: Try to start bg work");

				if (bgWorker.IsBusy)
				{
					Debug.WriteLine("bgWorker is busy, cancel async");
					bgWorker.CancelAsync();
				}

				Debug.WriteLine("set_selectedItem: Startint bg work");
				while (bgWorker.IsBusy) Thread.Sleep(100);
				bgWorker.RunWorkerAsync(_selectedItem);
				Debug.WriteLine("set_selectedItem: Bg work started");
			}




Вот прям печальный сеттер в ui-потоке. Во-первых, он огромный, нужен отдельный метод в tViewModel, чтобы перезапускать BackgroundWorker с нужным параметром. Во-вторых, судя по коду, он всегда вызывается в UI-потоке, стало быть lock не нужен. В-третьих, блокировка UI-потока, бесконечно ожидая чего-либо - прям вообще плохо.

Я так понимаю, BackgroundWorker используется просто, чтоб изучать асинхронность? Рекомендую сразу же начать с Task.

13thСтупил, теперь разобрался. Спасибо за внимание.


Хороший тон общения на форуме - написать, как решил проблему. У кого-то может возникнуть похожий вопрос в будущем
...
Рейтинг: 0 / 0
BackgroundWorker - не завершается
    #39413667
ВМоисеев
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
>Pallaris, сегодня, 06:11 [20259193]
>...Хороший тон общения на форуме ...

Видимо цикл
while (bgWorker.IsBusy) Thread.Sleep(100);
надо заменить на
while (bgWorker.IsBusy) Application.DoEvents();

Только при чем здесь BackgroundWorker ?
Как я понимаю, коллега блокировал вытесняющую многозадачность Windows.

С уважением,
Владимир.
...
Рейтинг: 0 / 0
BackgroundWorker - не завершается
    #39413697
13th
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Pallaris13th
Код: 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.
			set
			{
				lock (_selectedItem)
				{
					if (_selectedItem == value)
						return;

					_selectedItem = value;
				}

				OnPropertyChanged("selectedItem");
				if (_selectedItem == "")
					return;

				Debug.WriteLine("set_selectedItem: Try to start bg work");

				if (bgWorker.IsBusy)
				{
					Debug.WriteLine("bgWorker is busy, cancel async");
					bgWorker.CancelAsync();
				}

				Debug.WriteLine("set_selectedItem: Startint bg work");
				while (bgWorker.IsBusy) Thread.Sleep(100);
				bgWorker.RunWorkerAsync(_selectedItem);
				Debug.WriteLine("set_selectedItem: Bg work started");
			}




Вот прям печальный сеттер в ui-потоке. Во-первых, он огромный, нужен отдельный метод в tViewModel, чтобы перезапускать BackgroundWorker с нужным параметром. Во-вторых, судя по коду, он всегда вызывается в UI-потоке, стало быть lock не нужен. В-третьих, блокировка UI-потока, бесконечно ожидая чего-либо - прям вообще плохо.

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

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
			set
			{
				lock (_selectedItem)
					_selectedItem = value;

				OnPropertyChanged("selectedItem");
				if (_selectedItem == "")
					return;

				if (bgWorker.IsBusy)
					bgWorker.CancelAsync();

				while (bgWorker.IsBusy) Thread.Sleep(100);
				bgWorker.RunWorkerAsync(_selectedItem);
			}


Не такой он уж и "огромный". Конечно, конструкция "while (bgWorker.IsBusy) Thread.Sleep(100);" в данном контексте - это тупизм, но я писал уже вечером, поэтому немного протупил, чего уж там.
Что даст вынос кода из сеттера? Время выполнения не уменьшит.
lock - нужен, из разных потоков обращаемся к этим данным.

PallarisЯ так понимаю, BackgroundWorker используется просто, чтоб изучать асинхронность? Рекомендую сразу же начать с Task.

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

PallarisХороший тон общения на форуме - написать, как решил проблему. У кого-то может возникнуть похожий вопрос в будущем

Судя по количеству отзывов не подумал, что это кому-то интересно. Я сделал вот так:

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
				if (bgWorker.IsBusy)
				{
					bgWorker.CancelAsync();

					System.Threading.ThreadStart startPoint = new System.Threading.ThreadStart(delegate()
					{
						while (bgWorker.IsBusy) System.Threading.Thread.Sleep(50);
						lock (_selectedItem) bgWorker.RunWorkerAsync(this);
					});

					Thread waitingThread = new Thread(startPoint);
					waitingThread.Start();
				}
				else
				{
					bgWorker.RunWorkerAsync(this);
				}
...
Рейтинг: 0 / 0
BackgroundWorker - не завершается
    #39413703
13th
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
ВМоисеевВидимо цикл
while (bgWorker.IsBusy) Thread.Sleep(100);
надо заменить на
while (bgWorker.IsBusy) Application.DoEvents();


Советы на добавление "Application.DoEvents();" давным-давно надо оборачивать в "если вы конечно, не используете WPF".

ВМоисеевТолько при чем здесь BackgroundWorker ?
Как я понимаю, коллега блокировал вытесняющую многозадачность Windows.
"блокировал вытесняющую многозадачность" - звучит эпично! Похоже, теперь мой компьютер работает в однозадачном режиме!
...
Рейтинг: 0 / 0
BackgroundWorker - не завершается
    #39413979
ВМоисеев
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
>13th, сегодня, 15:02 http://www.sql.ru/forum/actualutils.aspx?action=gotomsg&tid=1251745&msg=20260641][20260641]
>...Я сделал вот так: ...

А если так:
Код: 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.
private void bg_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
  if (e.Cancelled)
    Debug.WriteLine("bg_RunWorkerCompleted: bg work canceled");
  else
    Debug.WriteLine("bg_RunWorkerCompleted: bg work ended with result " + e.Result.ToString());
  
  //-- Предыдущий рабочий поток завершен. Нужно ли запускать следующий?  
  if (_selectedItem != "")  bgWorker.RunWorkerAsync(_selectedItem);  //-- Да

}

private string _selectedItem = "";

public string selectedItem	{
  get	{ return _selectedItem; }
  set	{
    lock (_selectedItem) {
      if (_selectedItem == value)	return;
      _selectedItem = value;
    }

    OnPropertyChanged("selectedItem");
    if (_selectedItem == "")	return;

    Debug.WriteLine("set_selectedItem: Try to start bg work");

    if (bgWorker.IsBusy) {
      Debug.WriteLine("bgWorker is busy, cancel async");
      bgWorker.CancelAsync();
    }
    else {
      bgWorker.RunWorkerAsync(_selectedItem);
      Debug.WriteLine("set_selectedItem: Bg work started");
    }
}
...
Рейтинг: 0 / 0
BackgroundWorker - не завершается
    #39414016
Pallaris
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
13th
Код: c#
1.
2.
				lock (_selectedItem)
					_selectedItem = value;




В этой конструкции нет никакого смысла. Допустим первый поток залочил доступ, второй поток уперся в lock. Потом первый поток присвоил в _selectedItem значение и отдал управление второму. Второй тоже присвоил что-то в _selectedItem и дальше они начали по очереди выполнять сеттер с каким-то одним значением.
...
Рейтинг: 0 / 0
BackgroundWorker - не завершается
    #39414021
Pallaris
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Более того, даже если ты весь сеттер обернешь в лок, то все равно множество потоков будут вынолять его тело, т.к. ты перезаписываешь объект в памяти, на которой ты лочишься. Вот простой пример:
Код: 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.
class Program
    {
        static void Main(string[] args)
        {
            var worker = new Worker();
            var list = new List<Task>();
            list.Add(Task.Factory.StartNew(() => { while (true) worker.DoWork("a"); }));
            list.Add(Task.Factory.StartNew(() => { while (true) worker.DoWork("b"); }));
            list.Add(Task.Factory.StartNew(() => { while (true) worker.DoWork("c"); }));
            Task.WaitAll(list.ToArray());

        }
    }

    internal class Worker
    {
        private string _badLock = string.Empty;
        private object _goodLock = new object();

        public void DoWork(string value)
        {
            lock (_badLock)
            {
                _badLock = value;

                if (_badLock != value)
                    Console.WriteLine("Error!");
            }
        }
    }
...
Рейтинг: 0 / 0
BackgroundWorker - не завершается
    #39414027
ВМоисеев
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
>13th, вчера, 15:09 http://www.sql.ru/forum/actualutils.aspx?action=gotomsg&tid=1251745&msg=20260669][20260669]
>...Похоже, теперь мой компьютер работает в однозадачном режиме!...

Я был не прав.

Но как может получить управление оператор bgWorker.RunWorkerAsync(_selectedItem); в последовательности

. . .
if (bgWorker.IsBusy) bgWorker.CancelAsync();
while (bgWorker.IsBusy) Thread.Sleep(100);

bgWorker.RunWorkerAsync(_selectedItem);
. . .

после завершения рабочего потока ?
bgWorker перезапустит UI-поток с метода RunWorkerCompleted.
...
Рейтинг: 0 / 0
BackgroundWorker - не завершается
    #39414569
13th
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Pallaris13th
Код: c#
1.
2.
				lock (_selectedItem)
					_selectedItem = value;




В этой конструкции нет никакого смысла. Допустим первый поток залочил доступ, второй поток уперся в lock. Потом первый поток присвоил в _selectedItem значение и отдал управление второму. Второй тоже присвоил что-то в _selectedItem и дальше они начали по очереди выполнять сеттер с каким-то одним значением.

У меня пишет в _selectedItem только первый поток. Остальные - один читает, а второй - использует просто как объект синхронизации. Так что, по-моему тут всё норм.
...
Рейтинг: 0 / 0
BackgroundWorker - не завершается
    #39414584
13th
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Pallaris, А, понял. Спасибо, я понял, где ошибка.
...
Рейтинг: 0 / 0
BackgroundWorker - не завершается
    #39414637
13th
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
ВМоисеев, ну, в таком алгоритме тоже есть некоторые ошибки. Например, одна и та же задача будет постоянно перезапускаться по кругу.
...
Рейтинг: 0 / 0
BackgroundWorker - не завершается
    #39414642
13th
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
ВМоисеев>13th, вчера, 15:09 http://www.sql.ru/forum/actualutils.aspx?action=gotomsg&tid=1251745&msg=20260669][20260669]

Но как может получить управление оператор bgWorker.RunWorkerAsync(_selectedItem); в последовательности

. . .
if (bgWorker.IsBusy) bgWorker.CancelAsync();
while (bgWorker.IsBusy) Thread.Sleep(100);

bgWorker.RunWorkerAsync(_selectedItem);
. . .

после завершения рабочего потока ?

Ну, в том-то и состояла моя ошибка, что никак. Поэтому я и вынес перезапуск в отдельный поток, что бы bg_RunWorkerCompleted доработал.

ВМоисеевbgWorker перезапустит UI-поток с метода RunWorkerCompleted.
Вот эту фразу я не понял. Может быть, имелось ввиду " bgWorker не сможет запустить RunWorkerCompleted, т.к. UI-поток занят while (bgWorker.IsBusy) Thread.Sleep(100); " ?
...
Рейтинг: 0 / 0
BackgroundWorker - не завершается
    #39414724
ВМоисеев
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
>13th, сегодня, 12:25 http://www.sql.ru/forum/actualutils.aspx?action=gotomsg&tid=1251745&msg=20266553] [20266553]
>Вот эту фразу я не понял ...

Да я и сам не вполне уверен в её истинности.
Можно попытаться проверить.
1. Остановить в отладчике на операторе bgWorker.CancelAsync();
2. Установить точку останова на операторе метода RunWorkerCompleted;
3. F5.

С уважением,
Владимир
...
Рейтинг: 0 / 0
BackgroundWorker - не завершается
    #39414880
ВМоисеев
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
>13th, сегодня, 12:22 http://www.sql.ru/forum/actualutils.aspx?action=gotomsg&tid=1251745&msg=20266525][20266525]
>... в таком алгоритме тоже есть некоторые ошибки ...

Алгоритм - сильно сказано. Это скорее подсказка - рассмотри другие варианты. Судя по описанию работы BackgroundWorker, его инициализацию можно производить, если DoWork завершен (рабочий поток) и начал работать (отработал) RunWorkerCompleted (UI-поток).
А почему бы здесь и инициализировать новый рабочий поток?
Вас смущает случай, если рабочий поток завершится самостоятельно, а не будет прерван оператом?

А если ввести ещё одну переменную?

private string _selectedItem = ""; //-- заявка на обслуживание
private string _обработка = ""; //-- обработка предыдущей заявки

А в завершении RunWorkerCompleted проверить на равенство?

С уважением,
Владимир
...
Рейтинг: 0 / 0
BackgroundWorker - не завершается
    #39415474
13th
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Кстати, а что будет с BackgroundWorker, если его хост закрывается? Похоже, просто прерывает поток и умирает. В Output ничего не пишет, процесс завершается сразу, не ждёт завершения длинной задачи.
...
Рейтинг: 0 / 0
21 сообщений из 21, страница 1 из 1
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / BackgroundWorker - не завершается
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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