BackgroundWorker - CancellationPending се променя на false в RunWorkerCompleted. Защо?

След анулиране на BackGroundWorker, в DoWork, CancellationPending е true, но когато той стигне до RunWorkerCompleted, CancellationPending е false. Не знам какво направих погрешно?

static BackgroundWorker b1;

static void Main(string[] args)
{
    b1=new BackgroundWorker();
    b1.DoWork += new DoWorkEventHandler(work1);
    b1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(completed);
    b1.WorkerSupportsCancellation = true;
    b1.RunWorkerAsync("Hellow");
    Console.ReadLine();
}

private static void completed(object sender, RunWorkerCompletedEventArgs e)
{
    if (((BackgroundWorker)sender).CancellationPending)
        Console.WriteLine("Canceled!");
    else
        Console.WriteLine("Result:" + e.Result);//it goes here every time
}

private static void work1(object sender, DoWorkEventArgs e)
{
    ((BackgroundWorker)sender).CancelAsync();
    if (((BackgroundWorker)sender).CancellationPending)
    {
        e.Cancel = true;
    }
}

Между другото, как мога да добавя грешка, възникнала в DoWork, към RunWorkerCompletedEventArgs.Error, за да я предам на потребителя?


person Stav Alfi    schedule 29.07.2012    source източник


Отговори (2)


Вярвам, че свойството CancellationPending е за използване по време на фоновата операция (във вашия метод work1). Той ще каже на фоновия работник, че сте поискали фоновата операция да бъде отменена. След като бъде извикано събитието RunWorkerCompleted, фоновият работник е свършил работата, за да анулира заявката и следователно анулирането вече не е чакащо.

РЕДАКТИРАНЕ: RunWorkerCompletedEventArgs има свойство Canceled, което ще ви каже дали операцията на заден план е била отменена.

Ако хвърлите изключение от метода DoWork (work1 във вашия случай), то трябва да бъде уловено от BackgroundWorker и да попълни свойството Error на RunWorkerCompletedEventArgs.

person Brian S    schedule 29.07.2012
comment
Във всеки пример онлайн те използват CancellationPending в RunWorkerCompleted, за да проверят дали bgw е отменен. Благодаря ви за отделеното време, но не сте прави. - person Stav Alfi; 29.07.2012
comment
Всъщност @brian s е правилен. Трябва да проверите флага Canceled в параметъра RunWorkerCompletedEventArgs във вашата завършена функция. msdn.microsoft.com/en-us/library/ - person Surfbutler; 29.07.2012
comment
Да, благодаря @Surfbutler, забравих за флага за отменено. - person Brian S; 29.07.2012
comment
хо! Разбрах, благодаря момчета и съжалявам, Brain S. Благодаря ви отново. - person Stav Alfi; 29.07.2012
comment
Без притеснения, ако си спомнях флага за отменено, нямаше да е толкова объркващо. Радвам се, че се получи. Ще редактирам отговора, за да не обърквам другите в бъдеще. - person Brian S; 29.07.2012

Да, класът BackgroundWorker задава свойството CancellationPending на false, преди да предизвика събитието RunWorkerCompleted. Независимо дали работникът действително е бил анулиран или не.

Това е съвсем умишлено, предпазва ви от попадане в неприятен капан, който винаги е наоколо, когато използвате нишки. Кодът, който използва нишки, често се държи неправилно произволно и непредсказуемо поради един вид грешка, наречена „надпревара в нишките“. Това е много често срещан вид грешка и ужасно трудна за отстраняване на грешки.

Това, което лесно може да се обърка във вашия планиран подход, ако BGW не направи това, е, че ще приемете, че работникът е бил отменен, когато видите CancellationPending, зададен на true. Но това е илюзия, не можете да направите разлика между това да бъде отменено и да завърши нормално. Ъгловият случай е, че извиквате CancelAsync() микросекунда преди работникът да завърши. Работникът никога няма шанс дори да види флага CancellationPending, зададен на true, той беше зает с довършването на последните битове от метода за обработка на събития DoWork. Това е състезание с нишки, работникът се състезава пред вашето повикване и завърши нормално.

Правилното ръкостискане, което избягва този бъг, е вашият работник да зададе e.Cancel на true, когато види свойството CancellationPending, зададено на true. И разбира се спиране на това, което прави. Сега вече е надеждно, свойството e.Cancelled в манипулатора на събитие RunWorkerCompleted е копие на e.Cancel. Така че вашият код вече може надеждно да ви каже дали работникът е видял или не заявката за отмяна.

person Hans Passant    schedule 29.07.2012
comment
ако предадете вашия BackgroundWorker обект наоколо, ще трябва да предадете и DoWorkEventArgs с него, за да направите това, което Ханс предлага. Обикновено извиквам BackgroundWorker worker и DoWorkEventArgs dwea - тогава може да имам контекст, където правя: if(worker.CancellationPending){dwea.Cancel = true; return;} - person Jesse Adam; 25.01.2017