LoneRay
@LoneRay
Начинающий кодировщик.

Почему сохраняются старые значения в потоке?

Помогите пожалуйста, почему в потоке BackgroundWorker сохраняются старые значения?
BackgroundWorker bw = new BackgroundWorker();
private void button2_Click(object sender, EventArgs e) // запуск таймера класс BackgroundWorker
        {
            key = true;
            int i = 0;
            Text = "Таймер. Время: " + i;
            Timer time = new Timer();
            bw.DoWork += (o, eo) => // событие потока (событие работает до того момента пока не завершится действие)
            {
                for (;key;)
                {
                    Invoke(new Action(() => // делегат позволяет получить безопасный доступ к элементам главного потока, то есть из потока в котором был запущен поток
                    {
                        i = time.Time(i);
                        Text = "Таймер. Время: " + i;
                    }));
                    Thread.Sleep(1000);
                }
            };
            bw.RunWorkerAsync(); // старт потока
        }
private void button3_Click(object sender, EventArgs e)
        {
            if (bw != null) // если поток запущен
            {
                key = false;
                bw.WorkerSupportsCancellation = true; // при завершении события DoWork WorkerSupportsCancellation устанавливается true, это нужно для того, чтобы завершить поток. Но так как DoWork не завершён, то мы принудительно указываем true
                bw.CancelAsync(); // уничтожение потока
            }
        }
public class Timer // типо таймер
    {
        public int Time(int i)
        {
            i++;
            return i;
        }
    }
  • Вопрос задан
  • 128 просмотров
Решения вопроса 2
DarkRaven
@DarkRaven
разработка программного обеспечения
Я внес небольшие изменения:
bool key;
Timer time; /*вынес класс*/
volatile int i; /*вынес i и пометил как volotile, но если у вас Intel и один поток, то это не обязательно*/

BackgroundWorker bw = new BackgroundWorker();
private void button2_Click(object sender, EventArgs e) // запуск таймера класс BackgroundWorker
{

У меня текст менялся - Таймер. Время: 1, Таймер. Время: 2 и т.д.

Вопрос, у вас какой процессор?
Представленный пример кода - это упрощенная версия? Если да, вы его проверяли?
У вас только один поток меняет значение переменной i?
Ответ написан
AlekseyNemiro
@AlekseyNemiro
full-stack developer
Вы используете экземпляр BackgroundWorker, который живет на уровне класса.

Всякий раз, когда вы нажимаете на кнопку, к экземпляру BackgroundWorker подключается новый обработчик события DoWork. Т.е. нажмете один раз на кнопку, будет один обработчик, нажмете два раза, то уже будет два обработчика, нажмете десять раз - будет десять обработчиков и все они будут работать. Это будет хорошо видно, когда обработчиков будет много, из-за рассинхронизации значение i будет меняться быстрее, хаотичней. Так что вывод переменной на уровень класса и присваивание ей нулевого значения - это не решение проблемы.

В данном случае, можно либо всякий раз при нажатии на кнопку создавать новый экземпляр BackgroundWorker:

private void button2_Click(object sender, EventArgs e) // запуск таймера класс BackgroundWorker
{
  // создаем новый экземпляр BackgroundWorker
  bw = new BackgroundWorker();
  // остальной код
  key = true;
  // ...
}

Либо отключать обработчик, но для этого придется иметь ссылку на него (bw.DoWork -= ссылкаНаОбработчик). При таком варианте проще отказаться от анонимных функций и использовать обычные.

Если интересно, то количество обработчиков события DoWork у экземпляра BackgroundWorker можно проверить так:

EventHandlerList events = (EventHandlerList)typeof(Component).GetProperty
(
  "Events", 
  BindingFlags.NonPublic | BindingFlags.Instance
).GetValue(bw, null);

var k = typeof(BackgroundWorker).GetField
(
  "doWorkKey", 
  BindingFlags.NonPublic | BindingFlags.Static
).GetValue(null);

var handlers = events[k];

Console.WriteLine
(
  "Обработчиков {0}", 
  handlers.GetInvocationList().Length
);
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы