Skoro svaki korisnik PC računara kod svoje kuće koristi računar sa jednim CPU – Central Processing Unit – centralnim procesorom, koji u suštini obavlja samo jednu operaciju u datom vremenu. Zbog velike brzine procesora, mi često imamo utisak da na primer računar obavlja više poslova istovremeno kad otvorimo više aplikacija u Windows-u. Međutim, činjenica je dok se jedna operacija obavlja, druge čekaju. Na primer; kad vam se pojavi neki veliki bag u računaru, to često znači da se neka operacija na procesorskom nivou izvršava previše vremena ili se ne može izvršiti do kraja i zbog toga vam tada izgleda da vam je celi Windows smrznut i slika na monitoru ukočena, sve dok ne resetujete računar.
( Životni ciklus niti u C# programskom jeziku )
Da se ovo nebi stalno dešavalo da svaka aplikacija zamrzne celi Windows pri svakoj komplikaciji tokom rada, stvoren je koncept korišćenja niti. Svaka aplikacija u Windows-u pokreće svoj vlastiti proces koji je izolovan od drugih aplikacija. Znači, svaki proces pokreće vlastiti thread – nit; nit shvatite jednostavno kao virtualni CPU. Nit je sekvenca izvršavanja programa. Većina programa koje programirate u C# programskom jeziku ima jednu ulaznu tačku, to je metoda Main() i završava se povratkom iz metode. Međutim, ni jedan Internet pretraživač ne radi na taj način. Oni kao da izvršavaju više zadataka istovremeno. I vi ćete dolaziti u situacije u vašim programima da vam je potrebno da obavljate neke funkcije bar prividno istovremeno. Svaka nit u Windows-u ima svoje određeno vreme koje se meri u milisekundima i nakon tog perioda nit se pauzira i Windows se prebacuje na drugu nit. Takvo ponašanje zovemo Context Switching. Koristeći niti mi izvršavamo procese bez čekanja da se prethodni proces završi. Na taj način mi samo stvaramo iluziju da se izvršava više niti u isto vreme. Kod više procesorskih računara gde imamo više CPU, koristimo Parallelism – paralelizam; gde se niti izvršavaju paralelno na više različitih CPU. Na koje sve načine možete da izvršavate Multithreating – izvršavanje više niti; najbolje da vidite šta vam .Net Framework nudi.
Kako se kreira nit koristeći Thread klasu?
Da bi ste najjednostavnije manipulisali nitima koristite Thread klasu koja se nalazi u System.Threading imenskom prostoru. Instanca klase Thread predstavlja jednu nit; jednu sekvencu izvršavanja. Drugu nit možete da formirate tako što konkretizujete objekat niti. Klasa Thread vam jednostavno omogućava da kreirate nove niti, kontrolišete njihova svojstva i dobijate status niti. Opet treba da znate da niti treba da koristite samo ako imate dobar razlog za to. Pogledajte praktičan primer kako se pravi nit.
using System;
using static System.Console;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ThreadClass
{
class Program
{
public static void ThreadMethod()
{
for
(sbyte i = 1; i < 5; i++)
{
WriteLine($"A: {i + 1}");
Thread.Sleep(0);
}
}
static void Main(string[] args)
{
Thread t = new Thread(new ThreadStart(ThreadMethod));
t.Start();
for
(sbyte i = 1; i < 5; i++)
{
WriteLine($"B: {i + 1}");
Thread.Sleep(0);
}
t.Join();
WriteLine(Environment.NewLine + "Press any key to
continue...");
ReadKey();
}
}
}
B: 1
A: 1
B: 2
A: 2
B: 3
A: 3
B: 4
A: 4
B: 5
A: 5
Press any key to continue...
Kako navedeni program izgleda možete pogledati i na video-u:
( C# 6.0 Tutorial - Advanced - 6. Thread Class )
Kako da kreiram nit u background-u?
Postoje dve vrste niti, foreground thread – nit koja se izvršava u prvom planu i background thread - nit koja se izvršava u pozadini. Mnogo je bitno znati razliku. Po defaultu sve niti se izvršavaju u prvom planu i to jednostavno znači da se vaša aplikacija ne može ugasiti dok se sve niti ne izvrše. Ukoliko ste kreirali nit koja radi u pozadini, vaša aplikacija se može ugasiti i ako nit nije izvršena. Da bi se vaša nit izvršavala u pozadini dovoljno je podesiti properti IsBackground na true pre pokretanja niti. Pogledajte sledeći program.
using System;
using static System.Console;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ThreadBackground
{
class Program
{
public static void ThreadMethod()
{
for
(sbyte i = 1; i < 5; i++)
{
WriteLine($"Process: {i + 1}");
Thread.Sleep(1000);
}
}
static void Main(string[] args)
{
Thread t = new Thread(new ThreadStart(ThreadMethod));
t.IsBackground = true; // the application won't wait
until the background threads are completed
t.Start();
// WriteLine(Environment.NewLine + "Press any key to
continue..." + Environment.NewLine);
// ReadKey();
}
}
}
( C# 6.0 Tutorial - Advanced - 7. Thread Background )
Kako da kreiram parametorizovan start niti?
Ukoliko vam je potrebno da vi parametrima utičete na izvršavanje niti, to je moguće jer konstruktor Thread se može nadjačati delegatom ParameterizedThreadStart. Pogledajte sledeći kod:
using System;
using static System.Console;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ParameterizedThread
{
class Program
{
public static void ThreadMethod(object o)
{
for
(int i = 0; i < (int)o; i++)
{
WriteLine($"Process: {i + 1}");
Thread.Sleep(1000);
}
}
static void Main(string[] args)
{
Thread t = new Thread(new ParameterizedThreadStart(ThreadMethod));
t.Start(5);
t.Join();
WriteLine(Environment.NewLine + "Press any key to
continue...");
ReadKey();
}
}
}
Process: 1
Process: 2
Process: 3
Process: 4
Process: 5
Press any key to continue...
Kako navedeni program izgleda možete pogledati i na video-u:
( C# 6.0 Tutorial - Advanced - 8. ParameterizedThreadStart )
Kako da zaustavim nit?
Da bi ste zaustavili nit vi možete koristiti Abort() metod, ali pošto ovu nit izvršavate preko druge niti to može izazvati grešku, tako da je najbolje rešenje i praksa da koristite deljenu promenjivu. Nemojte da vas zbunjuje pisanje lambda izraza za skraćeno pisanje verzije delegata. Za sada samo obratite pažnju na koje sve načine može skraćeno da se kodira i kako to rade profesionalci u programiranju i pokušajte da nešto od toga i sami usvojite u vašem kodiranju. Pogledajte sledeći kod:
using System;
using static System.Console;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace StoppingThread
{
class Program
{
static void Main(string[] args)
{
bool
stopped = false;
Thread t = new Thread(new ThreadStart(() =>
{
while (!stopped)
{
WriteLine("Running...");
Thread.Sleep(2000);
}
}));
t.Start();
WriteLine("Press any key to
continue..."
+ Environment.NewLine);
ReadKey();
stopped = true;
t.Join();
}
}
}
Press any key to continue...
Running...
Running...
Running...
Running...
Running...
Running...
...
Kako navedeni program izgleda možete pogledati i na video-u:
( C# 6.0 Tutorial - Advanced - 9. Stopping Thread )
Da li mogu da koristim atribut umesto deljene promenjive?
Možete. Čak je i praksa da se koristi atribut ThreadStatic umesto deljene promenjive koji imamo zahvaljujući klasi ThreadStaticAttribute. Pogledajte sledeći kod koji omogućava svakoj niti njenu vlastitu kopiju polja.
using System;
using static System.Console;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace UsingThreadStaticAttribute
{
class Program
{
[ThreadStatic]
public static int field;
static void Main(string[] args)
{
new
Thread(() =>
{
for (sbyte i = 0; i < 5; i++)
{
field++;
WriteLine($"A: {field}");
}
}).Start();
new
Thread(() =>
{
for (sbyte i = 0; i < 5; i++)
{
field++;
WriteLine($"B: {field}");
}
}).Start();
WriteLine(Environment.NewLine + "Press any key to
continue..."
+ Environment.NewLine);
ReadKey();
}
}
}
Kad pokrenete navedeni program, vaš rezultat će biti sličan:
A: 1
A: 2
A: 3
A: 4
A: 5
Press any key to continue...
B: 1
B: 2
B: 3
B: 4
B: 5
Obratite pažnju da bez atributa ThreadStatic, naše statično polje field bi maksimalno umesto 5, iznosilo 10.
A: 1
A: 2
A: 3
A: 4
A: 5
B: 6
Press any key to continue...
B: 7
B: 8
B: 9
B: 10
Kako navedeni program izgleda možete pogledati i na video-u:
( C# 6.0 Tutorial - Advanced - 8. ParameterizedThreadStart )
Šta je ThreadLocal<T> ?
Ako želite da koristite lokalne podatke i inicijalizujete ih za svaku nit posebno, onda možete koristiti klasu ThreadLocal<T> . U ovom slučaju klasa zahteva delegat od metode koja inicijalizuje vrednost. Pogledajte sledeći primer.
using System;
using static System.Console;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace UsingThreadLocal
{
class Program
{
public static ThreadLocal<int> field =
new
ThreadLocal<int>(() =>
{
return Thread.CurrentThread.ManagedThreadId;
});
static void Main(string[] args)
{
new
Thread(() =>
{
for (sbyte i = 0; i < field.Value;
i++)
{
WriteLine($"A: {i}");
}
}).Start();
new
Thread(() =>
{
for (sbyte i = 0; i < field.Value;
i++)
{
WriteLine($"B: {i}");
}
}).Start();
WriteLine(Environment.NewLine + "Press any key to
continue..."
+ Environment.NewLine);
ReadKey();
}
}
}
A: 0
A: 1
A: 2
A: 3
A: 4
A: 5
A: 6
A: 7
A: 8
A: 9
A: 10
Press any key to continue...
B: 0
B: 1
B: 2
B: 3
B: 4
B: 5
B: 6
B: 7
B: 8
B: 9
B: 10
B: 11
Kako navedeni program izgleda možete pogledati i na video-u:
( C# 6.0 Tutorial - Advanced - 11. ThreadLocal )
Šta je Thread pool?
Kada radite direktno sa Thread klasom, vi kreirate novu nit svaki put i nit umre kad završite sa njom. Međutim takvo ponašanje takođe može da uzima dosta vremena i memorije. ThreadPool je kreiran da produži niti. Umesto da nit umre kad bude završena, vi je šaljete ponovo u ThreadPool odakle može biti ponovo korišćena. Kad koristite ThreadPool vi stavljate u red niti gde se izvršava nit prema svojoj dostupnosti. Zato što je ThreadPool brojčano limitiran on se ne koristi mnogo ali svakako ima svojih prednosti i vrlo se jednostavno kodira.
using System;
using static System.Console;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace UsingThreadPool
{
class Program
{
static void Main(string[] args)
{
ThreadPool.QueueUserWorkItem((s) =>
{
WriteLine("Working on a thread
from the threadpool");
});
WriteLine(Environment.NewLine + "Press any key to
continue..."
+ Environment.NewLine);
ReadKey();
}
}
}
Press any key to continue...
Working on a thread from the threadpool!
Kako navedeni program izgleda možete pogledati i na video-u:
( C# 6.0 Tutorial - Advanced - 12. ThreadPool )
No comments:
Post a Comment