Szoftverfejlesztés .NET platformon - Jegyzet 7. fejezet

A VIK Wikiből

Ez az oldal a korábbi SCH wikiről lett áthozva.

Ha úgy érzed, hogy bármilyen formázási vagy tartalmi probléma van vele, akkor, kérlek, javíts rajta egy rövid szerkesztéssel!

Ha nem tudod, hogyan indulj el, olvasd el a migrálási útmutatót.


70-536 .NET framework 2.0 Application Development Foundation

%TOC{depth="3"}%

Szálak

=System.Threading= névtér

Előny: felhasználói élmény növelése, nagyobb processzor kihasználtság.
A szálak memóriaterülete közös, a processzeké különböző.

Thread osztály

  • =IsAlive=: éppen fut-e
  • =IsBackground=: háttér szál-e. A háttér szálak a programból való kilépéskor automatikusan lelövődnek.
  • =IsThreadpoolThread=: thread pool-ból lett-e lekérve
  • =Name=: név lekérdezése és megváltoztatása
  • =Priority=: prioritás lekérdezése és megváltoztatása.

Lehetséges értékei: =Highest=, =AboveNormal=, =Normal=, =BelowNormal=, =Lowest=

  • =ThreadContext CurrentContext=
  • =CurrentPrincipal=: a szálat futtató felhasználó
  • =CurrentThread= (statikus)
  • =Start()=: szál indításának ütemezése
  • =Join()=: szál befejeződésének megvárása
  • =Sleep()=: aktuális szál várakoztatása (statikus)

ThreadState

  • =Suspended= (deprecated)
  • =Aborted=
  • =AbortRequested=
  • =Background=: az alkalmazás végén automatikusan lelövi
  • =Running=
  • =Stopped=
  • ...

Szálak indítása és leállítása

  1. =ThreadStart= objektum létrehozása (prioritás, stb. megadása)

(szálkezelő függvény: =void Foo()=) vagy
=ParameterizedThreadStart= létrehozása

(szálkezelő függvény:

void Foo(object parameter)

)

  1. =Thread= létrehozása
  2. Szál elindítása

Szál indítása paraméter nélkül

static void SimpleWork() {
	 Console.WriteLine("Thread: {0}", Thread.CurrentThread.ManagedThreadId);
}

Thread thread = new Thread(new ThreadStart(SimpleWork));
thread.Start();

Szál indítása paraméterrel

static void WorkWithParameter(object o) {
	 Console.WriteLine("{0}: {1}", o, Thread.CurrentThread.ManagedThreadId);
}

ParameterizedThreadStart operation = new ParameterizedThreadStart(WorkWithParameter);
Thread thread1 = new Thread(operation);
thread1.Start("Hello");
Thread thread2 = new Thread(operation);
thread2.Start("Goodbye");

Szál bevárása

A =thread.Join()= hívás blokkolja az aktuális szálat, amíg a =thread= szál futása be nem fejeződik.

Szál megszakítása

A =thread.Abort()= hívás megszakítja a szál futását. A futás aktuális pozícióján dob egy

ThreadAbortException

-t, amit a szálkezelő függvény elkaphat és lekezelhet. A szál megszakítása inkonzisztens állapothoz vezethet, ezért a szálkezelő rendszerrel tudatni kell, hogy hol szakítható meg a szál futása. A =Thread.BeginCriticalRegion()= és a =Thread.EndCriticalRegion()= közötti kód nem szakítható meg

Abort

-tal.

Execution Context

Tartalmazza

  • a futtató usert (=IPrincipal=),
  • a lokalizációs beállításokat és
  • a tranzakciós beállításokat.

Metódusai

  • =ExecutionContext.SuppressFlow()=: az automatikus context átadás kikapcsolása (gyorsítja a szál váltást)
  • =ExecutionContext.RestoreFlow()=: visszakapcsolás
  • =ExecutionContext.Capture()=: aktuális context lekérdezése
  • =ExecutionContext.Run(context, ContextCallback)=: metódus futtatása adott contexttel

Szinkronizáció

Ha a szálak közös memóriaterületet használnak, szinkronizálni kell a hozzáférést.

Könnyűsúlyú objektumok

=Interlocked= osztály: 5 műveletet tud atomi módon elvégezni

  • =Add()=: két szám összeadása
  • =Decrement()=: szám csökkentése
  • =Exchange()=: két szám megcserélése
  • =Increment()=: számot növel. Pl. =Interlocked.Increment(ref counter);=
  • =Read()=: 64 bites számot atomi műveletként olvas be

Monitor

  • =TryEnter()=: próbál lockolni, timeout megadható (pl.
    Timeout.Infinite
    )
  • =Enter(object)=: lockol, csak referencia típusú paramétert fogad el
  • =Exit(object)=: elengedi a zárat
  • =Wait()=: elengedi a zárat és vár, amíg nem kaphatja meg újra

=lock=:

Monitor

-ra fordul.

lock(obj) { 
	 op;
} 

jelentése

Monitor.Enter(obj); 
try {
	 op;
} finally {
	 Monitor.Exit(obj);
}

=ReaderWriterLock=: olvasási és írási lock külön kérhető

  • =IsReaderLockHeld=, =IsWriterLockHeld=
  • ={Acquire|Release}{ReaderWriter}Lock=. A zár kérésénél megadható timeout, ha lejár,
    ApplicationException
    -t dob.
  • ={UpgradeTo|DowngradeFrom}WriterLock=: olvasási ↔ írási lock átalakítás

Kernel objektumok

  • Közös ősosztály: =WaitHandle=
    • =WaitOne()=: elkérés
    • =Close()=: felszabadítás
  • =Mutex=:
    AppDomain
    -ek és processzek között is működik,

33x lassabb a

Monitor

-nál

    • =new Mutex(false, "CommonMutex")=: processzek közötti mutex létrehozása
    • =Mutex.OpenExisting("name")=: létező mutexre referencia kérése
  • =Semaphore=
    • =new Semaphore(currentSlots, maximumSlots)=: névtelen szemafor létrehozása
    • =new Semaphore(currentSlots, maximumSlots, name)=: névvel ellátott szemafor létrehozása
    • =Semaphore.OpenExisting("name")=: létező szemaforra referencia kérése

(ha nem létezik,

WaitHandleCannotBeOpenedException

-t dob)

  • =Event=: két állapota van: jelzett és jelzetlen. Várakozni lehet a jelzett állapotára.
    • Metódusai: =Set()=, =Reset()=
    • =AutoResetEvent=: azonos a működése a
      Mutex
      -szel?
    • =ManualResetEvent=: ha jelzettre vált, a
      Mutex
      -szel ellentétben az összes rá váró

szál továbbfut; csak =Reset()= hívásra vált vissza jelzetlenre.

Aszinkron programozás

=BeginXXX= és =EndXXX= metódusok.

Aszinkron hívási modellek

  • Wait-Until-Done: =EndXXX= meghívásakor megvárja a metódus befejeződését
  • Polling: A =BeginXXX=
    IAsyncResult
    -tal tér vissza, amit lehet kérdezgetni, hogy készen van-e (
    IsCompleted
    property)
  • Callback: befejeződéskor delegate viszahívása. A =BeginXXX= hívásakor át kell adni plusz paraméterként a callback függvényt.
    • Ha
      BeginRead
      -del olvasunk egy streamet, a callback függvénynek nem csak a puffert kell átadni, hanem a streamet is, hogy le tudja zárni.

Példa aszinkron olvasásra callback függvénnyel

static byte[] buffer = new byte[100];

static void AsyncRead() {
	 string filename = Environment.SystemDirectory + "\\mfc71.pdb";
	 FileStream fs = new FileStream(
		  filename, FileMode.Open, FileAccess.Read, FileShare.Read, 1024, FileOptions.Asynchronous);
	 IAsyncResult result = strm.BeginRead(
		  buffer, 0, buffer.Length, new AsyncCallback(CompleteRead), fs);
}

static void CompleteRead(IAsyncResult result) {
	 Console.WriteLine("Read Completed");
	 FileStream fs = (FileStream) result.AsyncState;
	 // az EndRead() nem fog blokkolódni
	 int numBytes = fs.EndRead(result);
	 fs.Close();
	 Console.WriteLine("Read {0} Bytes", numBytes);
	 Console.WriteLine(BitConverter.ToString(buffer));
}

Hibakezelés

Csak az =EndXXX= dobhat exceptiont, mivel a =BeginXXX= által dobott kivételeket a hívó nem tudja lekezelni.

Az aszinkron hívás során keletkező kivételek nem csak az =EndXXX= híváskor, hanem azonnal is lekezelhetők, ha feliratkozunk az =Application.ThreadException= eseményre.

Thread pool

Túl sok szál alkalmazása "vergődéshez" vezethet, azaz az operációs rendszer több időt tölt context switchinggel, mint a valódi végrehajtással. A =ThreadPool= használata javít a helyzeten

=ThreadPool=: háttér szálakat tárol.

  • =ThreadPool.QueueUserWorkItem(threadMethod, parameter)=: meghív egy metódust új szálon
  • =ThreadPool.UnsafeQueueUserWorkItem()=: -"-, de a context információt nem adja át
  • =ThreadPool.RegisterWaitForSingleObject()=:
    WaitHandle
    -höz lehet callback függvényt

regisztrálni

  • =ThreadPool.[Get|Set][MinMax]Threads()=
static void WorkWithParameter(object state) {
	 // ...
}

WaitCallback workItem = new WaitCallback(WorkWithParameter);
ThreadPool.QueueUserWorkItem(workItem, "ThreadPooled");

SynchronizationContext

Üzenetkezelésre használják

  • =SynchronizationContext.Current.Send()=: szinkron módon küld üzenetet
  • =SynchronizationContext.Current.Post()=: aszinkron módon küld üzenetet

(a Windows Forms szálkezelési modell ezt nem támogatja, ott a =Post()= is szinkron módon működik)

System.Threading.Timer

Ez az kernel szintű timer, a =System.Timers.Timer= és a =Windows.Forms.Timer= csak burkoló osztály. Pontossága: 55 ms

Konstruktor paraméterei:

  • =TimerCallback callback=
  • callback függvény argumentuma
  • =dueTime=: mennyi idő múlva hívja meg a callbacket először
  • =interval=: mennyi időnként hívja meg a callbacket
  • =Change(dueTime, interval)=: menet közben is megváltoztathatók az előbbi értékek. Leállítani úgy lehet, ha
    dueTime
    -nak
    Timeout.Infinite
    -et adunk meg.

-- Peti - 2007.06.27.