Esileht > Uncategorized > Rööpülesannete katkestamine

Rööpülesannete katkestamine


Rööpülesanded jooksevad üksteisest sõltumatult. Kui ühes ülesandes visatakse erand, ei takista see teistel ülesannetel edasi jooksmast. Erandid kogutakse kokku ja jõuavad pealõime.

Vaata seda näidet ühest kasutust programmist, kus iga lõim viskab poole täitmise pealt erandi:

static void Do()
{
  for (int i = 0; i < 100; i++)
    if (i >= 50)
      throw new OperationCanceledException();
}

static void Main(string[] args)
{
  try
  {
    Parallel.Invoke(Do, Do, Do);
  }
  catch (AggregateException x)
  {
    Console.WriteLine(x.InnerExceptions.OfType<OperationCanceledException>().Count() + " tasks canceled");
  }
}

Kõigi lõimede erandid kogutakse pealõime erandisse AggregateException. Ja selle InnerExceptions sisaldab meie näites täpselt sama palju erandeid nagu oli lõimesid. See tähendab, et iga lõim tegi oma tööd teistest sõltumatult ja jõudis oma tööga erandini.

Mida teha siis, kui üks lõim peaks saama teiste tegevuse katkestada? Näiteks, kui töös selgub viga, mis teeb mõttetuks kõigi teiste lõimede edasised pingutused. Teine variant on see, et kasutajal on “Loobu” nupp, millel klõpsates ta saab töö katkestada.

Siin tuleb appi CancellationTokenSource, mis on lõimeturvaline, spetsiaalselt selleks otstarbeks loodud objekt.

Meie järgmises näiteks on meil üldkasutatav muutuja

static CancellationTokenSource ts = new CancellationTokenSource();

ja igaüks (suvaline lõim või ka näiteks kasutaja, kes vajutab mingit nuppu) võib välja kutsuda ts.Cancel().

Ülesandeid luues on vaja kaasa anda Token:

Task.WaitAll
(
  Task.Factory.StartNew(Do, ts.Token),
  Task.Factory.StartNew(Do, ts.Token),
  Task.Factory.StartNew(Do, ts.Token)
);

Protseduur Do kasutab spetsiaalset meetodit ThrowIfCancellationRequested, mis kontrollib, kas ülesanded ei ole globaalselt katkestatud ja viskab erandi kui on.

static void Do()
{
  for (int i = 0; i < 100; i++)
  {
    ts.Token.ThrowIfCancellationRequested();
    if (i >= 50)
      ts.Cancel();
  }
}

Ülaltoodud näite tulemuseks on, et kõik ülesanded jõuavad katkestatud seisu, ent need katkestatakse hetkel, kui ükskõik milline lõimedest jõuab poole peale.

Mõned märkused

ThrowIfCancellationRequested vajab ressursse ja reaalelus ei oleks mõttekas tsükli igas sammus seda välja kutsuda. Näiteks kui tegemist on pikkade ülesannetega ja kasutaja vajutab katkestusnuppu, piisaks sellest, kui katkestamiseks kuluks üks sekund. Iga lõim peaks siis sel juhul kontrollima katkestust kord sekundis.

Teine asi on see, et ThrowIfCancellationRequested poolt visatavat erandit ei tohiks töödelda ülesandesiseselt, muidu see info ei jõua pealõimeni. Pealõimel on sellest kasu, sest Task-objekti Status seatakse sel juhul seisu TaskStatus.Canceled, mitte TaskStatus.Faulted nagu muude erandite puhul. Sel eesmärgil edastatakse Token ka Task.Factory.StartNew-meetodile.

Rubriigid:Uncategorized
  1. Kommentaare veel pole.
  1. No trackbacks yet.

Lisa kommentaar

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Muuda )

Twitter picture

You are commenting using your Twitter account. Log Out / Muuda )

Facebook photo

You are commenting using your Facebook account. Log Out / Muuda )

Google+ photo

You are commenting using your Google+ account. Log Out / Muuda )

Connecting to %s

%d bloggers like this: