loader
Foto

Bevezetés a szálak használatába C# nyelv segítségével

Áttekintjük most a szálkezelés alapjait. Az eddig bemutatott C# alkalmazások egy főszálat alkalmaztak, azaz, ha például meghívtunk egy metódust a "főszálból", addig a programunk "állt", hiszen a vezérlést a metódusunk kapta meg, ő használta a "főszálat".

Indítsuk el a Visual Studio-t és hozzunk létre egy Console alkalmazást, amelynek a neve legyen "ConsoleSzalak2" (1. ábra).

kep
1. ábra   Projekt létrehozása
 

Ahhoz, hogy a szálakat létre tudjunk hozni, illetve menedzselni, szükségünk van a "System.Threading" szerelvényre, vegyük ezt fel a projektünkbe (2. ábra).

kep
2. ábra   A System.Threading szerelvény hozzáadása a projektünkhöz
 

Másoljuk be a Program.cs file-ba a következő programot. A Main() metódus első sorában az aktuális szálnak (ez a főszálunk, nem készítünk itt még újat) adunk egy nevet. A szálnak tehát nem csak ID-ja lesz, hanem neve is.
Ezután kiírjuk a képernyőre a szálnak az ID-ját, és egyéb tulajdonságát is, például azt, hogy él-e a szál, háttérszál-e, de még a prioritását is.

class Program
    {
        static void Main(string[] args)
        {
            Thread.CurrentThread.Name = "WebElektronika";

            Console.WriteLine("Alkalmazás szálja : {0} ", Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("Szál CC-je : {0} ", Thread.CurrentThread.CurrentCulture);
            Console.WriteLine("Szál CC-jének a neve : {0} ", Thread.CurrentThread.CurrentCulture.DisplayName);
            Console.WriteLine("Él a szál? : {0} ", Thread.CurrentThread.IsAlive);
            Console.WriteLine("Háttérszál? : {0} ", Thread.CurrentThread.IsBackground);
            Console.WriteLine("Szál neve : {0} ", Thread.CurrentThread.Name);
            Console.WriteLine("Szál prioritása : {0} ", Thread.CurrentThread.Priority);
            Console.WriteLine("Szál állapota : {0} ",Thread.CurrentThread.ThreadState);

            Console.WriteLine("\nElindítjuk a notepad-et");

            Process process = Process.Start("notepad.exe");
            Thread.Sleep(4000);
            process.Kill();
            Console.WriteLine("Bezártuk a notepad-et");
            Console.ReadLine();
        }
    }

 

A szál néhány tulajdonságának a kiíratása után létrehozunk egy process-t, amelynek segítségével 4 másodpercre megjelenítjük a notepad alaklmazást (3. ábra).

kep
3. ábra   Futási eredmény, a főszálunk különböző adataival
 

Módosítsuk most úgy a programunkat, hogy készítünk egy metódust (Mutat()), amelyet különböző módokon fogunk meghívni. Másoljuk be a következő programot (felülírva az előzőt) a Program.cs file-ba.
A Mutat() metódus meghívása után először kiírjuk annak a szálnak az ID-ját, amelyben a metódus fut. Ezután a metódusban lévő for ciklusban százszor kiírjuk a metódusunk kezdőbetűjét sárga színnel úgy, hogy egy-egy "M" betű kiírása után várunk 15 ms-ot. A "Thread.Sleep" segítségével várakozunk, valójában a szál működési jogát vesszük el 15 ms-ra.

class Program
    {
        static void Mutat()
        {
            Console.WriteLine("Mutat : {0} ",Thread.CurrentThread.ManagedThreadId);
            
            for (int i = 0; i < 100; i++)
            {
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.Write("M");
                Thread.Sleep(15);
            }
        }

        static void Main(string[] args)
        {
            Console.WriteLine("Fő : {0} ", Thread.CurrentThread.ManagedThreadId);

            Mutat();

            for (int i = 0; i < 100; i++)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.Write("F");
                Thread.Sleep(15);
            }
            Console.ReadLine();
        }
    }

 

A Main() metódusban kiírjuk annak a szálnak az ID-ját, amelyben a Main() fut. Ezután meghívjuk a Mutat() metódust. Tekintettel arra, hogy a száljuk közös, ezért amíg a Mutat() metódus nem fut le, addig a Main()-ben található for ciklus nem indul el. 
Amikor kilépünk a Mutat() metódusból, akkor a for ciklusban megváltoztatjuk piros színre a szöveg színét, és ezután 15 ms-os várakozásokkal kiírjuk az "F" betűt.

Indítsuk el az alkalmazásunkat (F5). Látjuk a 4. ábrán, hogy a két metódus szála közös, először kiírjuk százszor az "M"-et, majd 100-szor az "F"-et. Látható tehát az, hogy a két metódusunk közös szálat használ.

kep
4. ábra   Futási eredmény, a szál közös (kattints a képre)
 

Módosítsuk most úgy a programunkat, hogy a Mutat() metódust külön szálban fogjuk futtatni. Ehhez szükséges a Thread osztály példányosítása (t1), és a példányosításkor a Thread osztály konstruktora paraméterként megkapja a Mutat() metódus nevét.

A Thread osztályból létrejött példánynak két olyan metódusa van, amelyet szálkezelésnél használnunk kell. A Start()-tal indítjuk el a szálat, illetve a Join()-nal várjuk be a szál végét.

A példányosítás után a t1.Start()-tal meghívjuk a Mutat() metódust egy külön szálon.

static void Main(string[] args)
        {
            Console.WriteLine("Fő : {0} ", Thread.CurrentThread.ManagedThreadId);

            Thread t1 = new Thread(Mutat);
            t1.Start();

            for (int i = 0; i < 100; i++)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.Write("F");
                Thread.Sleep(15);
            }

            t1.Join();
            Console.ReadLine();
        }

 

Indítsuk el az alkalmazásunkat. Látható, hogy az "F" és az "M" "össze-vissza" kerül kiírásra (5. ábra). Meg kell itt jegyeznünk, hogy a két szál egymástól függetlenül fut, viszont a Console.WriteLine() metódusok ugyanazt az konzol ablakot használják. (Tehát nem úgy működik, hogy egyszer az egyik, majd a másik szál működik.)

kep
5. ábra   Futási eredmény, a késleltetés 1 ms (kattints a képre)
 

Módosítsuk most a késleltetések értékt 1 ms-ról 15 ms-ra. Ekkor az látható, mintha egyszer az egyik szál, majd a másik szál működne, de a valóságban a két szál párhuzamosan fut, a helytelen megjelenítésért a WriteLine() a felelős (6. ábra).

kep
6. ábra  Futási eredmény, a késleltetés 15 ms (kattints a képre)
 

Nézzük most meg azt, hogy nem egy (háttér) szálat hozunk létre, hanem többet. Hívjuk meg például ötször a Mutat() metódust, mindegyiket természetesen külön szálban.

Ehhez a következő módosítások szükségesek :

class Program
    {
        static void Mutat(object obj)
        {
            Console.WriteLine("Mutat : {0} ",Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(20);
            for (int i = 0; i < 100; i++)
            {
                Console.ForegroundColor = (ConsoleColor)obj;
                Console.Write(Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(30);
            }
        }

        static void Main(string[] args)
        {
            Console.WriteLine("Fő : {0} ", Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(30);
            Thread[] tk = new Thread[5];
            for (int i = 0; i < tk.Length; i++)
            {
                tk[i] = new Thread(Mutat);
                tk[i].Start((ConsoleColor)10 + i);
            }


            for (int i = 0; i < 100; i++)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.Write("F");
                Thread.Sleep(30);
            }

            // összes szálat bevárjuk
            foreach (var item in tk)
            {
                item.Join();
            }

            Console.ReadLine();
        }
    }

 

Elindítjuk ötször külön szálakban a Mutat() metódust, és a foreach ciklusban a Join()-nal bevárjuk mind az öt szálat. Minden szálban kiírjuk az adott szál ID-t különböző színekkel (7. ábra).

A 7. ábrán látjuk azt futási eredményt, ahol nem volt késleltetés a Mutat() metódusban. 

kep
7. ábra   Futási eredmény, a késleltetés 0 ms (kattints a képre)
 

A "szebb" megjelenítés miatt alkalmaztunk a szálakban késleltetést, tehát a for ciklusban 30 ms-ora a szál működését felfüggesztettük (8. ábra).

kep
8. ábra   Futási eredmény, a késleltetés 30 ms (kattints a képre)
 

 



Egyéb cikkek

Mappa

További cikkeink ebben a témakörben

Régebbi cikkeink

Ebben a cikkben bemutatjuk a metódusok alapjait. Nem érintjük viszont például a túlterhelést, ezt egy következő részben tekintjük át.. . . .

Ebben a cikkben megismerjük a C# nyelv által használt változók nagy részét. Nézünk egy példát a típuskonverzióra.. . . .

A sorozatunknak ebben a részében átnézzük általánosságban az osztályok alapjait egy konzolalkalmazás segítségével. A konstruktorok viszont a következő részben kerülnek bemutatásra.. . . .