Nội dung chính
Đa luồng (Multithread) trong C#
[Thread trong c#] Một thread được định nghĩa như là một đường thực thi (execution path) của một chương trình. Mỗi Thread định nghĩa một dòng điều khiển duy nhất. Nếu application của bạn gồm các hoạt động phức tạp và tốn thời gian, thì nó thường là rất hữu ích khi thiết lập các execution path hoặc Thread, với mỗi Thread thực hiện một công việc cụ thể.
Các Thread là các tiến trình nhẹ. Một ví dụ phổ biến của sự sử dụng Thread là sự triển khai lập trình tương tranh (concurrent programming) bởi các hệ điều hành hiện đại. Sử dụng các Thread tiếp kiệm sự hao phí của CPU cycle và tăng hiệu quả của một application.
Tới chương này, chúng ta đã viết các chương trình mà một Thread đơn chạy như là một tiến trình đơn, đó là trình chạy thể hiện của application. Tuy nhiên, theo cách này, application có thể thực hiện một công việc tại một thời điểm. Để làm nó thực thi nhiều hơn một tác vụ tại một thời điểm, nó có thể được phân chia thành các Thread nhỏ hơn.
Vòng đời của Thread trong C#
Vòng đời của một Thread bắt đầu khi một đối tượng của lớp System.Threading.Thread được tạo và kết thúc khi Thread đó được kết thúc hoặc hoàn thành việc thực thi.
Dưới đây là các trạng thái đa dạng trong vòng đời của một Thread trong C#:
Unstarted State: Nó là tình huống khi instance của Thread được tạo, nhưng phương thức Start chưa được gọi.
Ready State: Nó là tình huống khi Thread đó sẵn sàng để chạy và đợi CPU cycle.
Not Runnable State: Một Thread là không thể thực thi (not executable), khi:
- Phương thức Sleep đã được gọi.
- Phương thức Wait đã được gọi.
- Bị ngăn chặn bởi hoạt động I/O.
Dead State: Nó là tình huống khi Thread hoàn thành sự thực thi hoặc bị hủy bỏ.
Main Thread trong C#
Trong C#, lớp System.Threading.Thread được sử dụng để làm việc với các Thread. Nó cho phép tạo và truy cập các Thread riêng biệt trong một Multithreaded Application. Thread đầu tiên để được thực thi trong một tiến trình được gọi là Main Thread trong C#.
Khi một chương trình C# bắt đầu thực thi, Main Thread được tự động tạo ra. Các Thread, được tạo bởi sử dụng lớp Thread, được gọi các Thread con của Main Thread. Bạn có thể truy cập một Thread bởi sử dụng thuộc tính CurrentThread của lớp Thread.
Dưới đây là chương trình ví dụ minh họa cho sự thực thi Main Thread trong C#:
using System; using System.Threading; namespace VietTutsCsharp { class Program { public static void Main() { Console.WriteLine("Da luong trong C#"); Console.WriteLine("-----------------------------------"); Thread th = Thread.CurrentThread; th.Name = "MainThread"; Console.WriteLine("Day la {0}", th.Name); Console.ReadKey(); } } }
Nếu bạn không sử dụng lệnh Console.ReadKey(); thì chương trình sẽ chạy và kết thúc luôn (nhanh quá đến nỗi bạn không kịp nhìn kết quả). Lệnh này cho phép chúng ta nhìn kết quả một cách rõ ràng hơn.
Biên dịch và chạy chương trình C# trên sẽ cho kết quả sau:
Thuộc tính và Phương thức của lớp Thread trong C#
Bảng dưới liệt kê một số thuộc tính được sử dụng phổ biến nhất của lớp Thread trong C#:
Thuộc tính | Mô tả |
---|---|
CurrentContext | Lấy ngữ cảnh (context) hiện tại mà trong đó Thread đang thực thi |
CurrentCulture | Lấy hoặc thiết lập culture gồm language, date, time, currency, … cho Thread hiện tại |
CurrentPrinciple | Lấy hoặc thiết lập nguyên lý hiện tại của Thread |
CurrentThread | Lấy Thread đang chạy hiện tại |
CurrentUICulture | Lấy hoặc thiết lập culture hiện tại được sử dụng bởi Resource Manager để tìm kiếm cho Resource cụ thể tại runtime |
ExecutionContext | Lấy một đối tượng ExecutionContext mà chứa thông tin về các context đa dạng của Thread hiện tại |
IsAlive | Lấy một giá trị chỉ trạng thái thực thi của Thread hiện tại |
IsBackground | Lấy hoặc thiết lập một giá trị chỉ rằng có hay không một Thread là Background Thread |
IsThreadPoolThread | Lấy một giá trị chỉ rằng có hay không một Thread là của Managed Thread Pool |
ManagedThreadId | Lấy một định danh duy nhất cho Managed Thread hiện tại |
Name | Lấy hoặc thiết lập tên của Thread |
Priority | Lấy hoặc thiết lập một giá trị chỉ quyền ưu tiên của một Thread |
ThreadState | Lấy một giá trị chứa các trạng thái của Thread hiện tại |
Bảng này liệt kê các phương thức được sử dụng phổ biến nhất của lớp Thread trong C#:
STT | Phương thức |
---|---|
1 |
public void Abort()
Tạo một ThreadAbortException trong Thread mà trên đó nó được triệu hồi, để bắt đầu tiến trình kết thúc Thread đó. Gọi phương thức này thường kết thúc Thread |
2 |
public static LocalDataStoreSlot AllocateDataSlot()
Cấp phát một Unnamed Data Slot cho tất cả Thread. Để tăng hiệu suất, sử dụng các Field mà được đánh dấu với attribute là ThreadStaticAttribute để thay thế |
3 |
public static LocalDataStoreSlot AllocateNamedDataSlot(string name)
Cấp phát một Named Data Slot cho tất cả Thread. Để tăng hiệu suất, sử dụng các Field mà được đánh dấu với attribute là ThreadStaticAttribute để thay thế |
4 |
public static void BeginCriticalRegion()
Thông báo cho một host rằng sự thực thi là chuẩn bị đi vào một khu vực code, mà trong đó các ảnh hưởng của việc hủy bỏ một Thread hoặc các Exception không được xử lý có thể gây nguy hại tới các tác vụ khác trong miền ứng dụng |
5 |
public static void BeginThreadAffinity()
Thông báo cho một Host rằng Managed code là chuẩn bị thực thi các chỉ lệnh mà phụ thuộc vào tính đồng nhất của Physical operating system thread hiện tại |
6 |
public static void EndCriticalRegion()
Thông báo cho một host rằng sự thực thi là chuẩn bị đi vào một khu vực code, mà trong đó các ảnh hưởng của hủy bỏ một Thread hoặc các Exception không được xử lý bị hạn chế tới tác vụ hiện tại |
7 |
public static void EndThreadAffinity()
Thông báo cho một Host rằng Managed code đã kết thúc việc thực thi các chỉ lệnh mà phụ thuộc vào tính đồng nhất của Physical Operating System Thread hiện tại |
8 |
public static void FreeNamedDataSlot(string name)
Loại bỏ sự liên kết giữa một name và một slot, cho tất cả Thread trong tiến trình. Để tăng hiệu suất, sử dụng các Field mà được đánh dấu với attribute là ThreadStaticAttribute để thay thế |
9 |
public static Object GetData(LocalDataStoreSlot slot)
Thu hồi giá trị từ slot đã xác định trên Thread hiện tại, bên trong miền hiện tại của Thread hiện tại. Để tăng hiệu suất, sử dụng các Field mà được đánh dấu với attribute là ThreadStaticAttribute để thay thế |
10 |
public static AppDomain GetDomain()
Trả về miền hiện tại trong đó Thread đang chạy |
11 |
public static AppDomain GetDomain()
Trả về một định danh miền ứng dụng duy nhất |
12 |
public static LocalDataStoreSlot GetNamedDataSlot(string name)
Tìm kiếm một Named Data Slot. Để tăng hiệu suất, sử dụng các Field mà được đánh dấu với attribute là ThreadStaticAttribute để thay thế |
13 |
public void Interrupt()
Interrupt (ngắt) một Thread mà trong trạng thái WaitSleepJoin |
14 |
public void Join()
Chặn Thread đang gọi tới khi một Thread kết thúc, trong khi tiếp tục thực hiện COM và SendMessage Pumping. Phương thức này có các mẫu được nạp chồng khác nhau |
15 |
public static void MemoryBarrier()
Đồng bộ truy cập bộ nhớ như sau: Prosessor đang thực thi Thread hiện tại không thể sắp xếp lại các chỉ lệnh theo một cách để mà quyền truy cập bộ nhớ tới lời gọi đến MemoryBarrier thực thi sau khi các truy cập bộ nhớ mà theo sau lời gọi đó đến MemoryBarrier |
16 |
public static void ResetAbort()
Hủy một Abort được yêu cầu cho Thread hiện tại |
17 |
public static void SetData(LocalDataStoreSlot slot, Object data)
Thiết lập dữ liệu trong slot đã cho trên Thread đang chạy hiện tại, cho miền hiện tại của Thread đó. Để tăng hiệu suất, sử dụng các Field mà được đánh dấu với attribute là ThreadStaticAttribute để thay thế |
18 |
public void Start()
Bắt đầu một Thread |
19 |
public static void Sleep(int millisecondsTimeout)
Làm Thread dừng trong một khoảng thời gian |
20 |
public static void SpinWait(int iterations)
Làm một Thread đợi một khoảng thời gian đã được xác định trong tham số iterations |
21 |
public static byte VolatileRead(ref byte address) public static double VolatileRead(ref double address) public static int VolatileRead(ref int address) public static Object VolatileRead(ref Object address) Đọc giá trị của một Field. Giá trị này là được viết mới nhất bởi bất kỳ Prosessor nào trong một máy tính, không quan tâm đến số lượng Prosessor hoặc trạng thái của Prosessor Cache. Phương thức này có các mẫu được nạp chồng khác nhau. Đó là các form ở trên |
22 |
public static void VolatileWrite(ref byte address,byte value) public static void VolatileWrite(ref double address, double value) public static void VolatileWrite(ref int address, int value) public static void VolatileWrite(ref Object address, Object value) Ghi một giá trị tới một Field ngay lập tức, để mà giá trị này là nhìn thấy cho tất cả Processor trong máy tính. Phương thức này có các mẫu được nạp chồng khác nhau. Đó là các form ở trên |
23 |
public static bool Yield()
Làm Thread đang gọi chuyển sự thực thi cho Thread khác mà đã sẵn sàng để chạy trên Processor hiện tại. Hệ điều hành chọn Thread để chuyển tới |
Tạo Thread trong C#
Trong C#, các Thread được tạo bằng việc kế thừa lớp Thread. Sau đó, Lớp Thread được kế thừa gọi phương thức Start() để bắt đầu sự thực thi của Thread con.
Sau đây là ví dụ minh họa việc tạo Thread trong C#:
using System; using System.Threading; namespace VietTutsCsharp { class Program { public static void CallToChildThread() { Console.WriteLine("Thread con bat dau!!!"); } static void Main(string[] args) { Console.WriteLine("Da luong trong C#"); Console.WriteLine("Vi du minh hoa cach tao Thread"); Console.WriteLine("----------------------------------"); ThreadStart childref = new ThreadStart(CallToChildThread); Console.WriteLine("Trong Main Thread: tao thread con."); Thread childThread = new Thread(childref); childThread.Start(); Console.ReadKey(); } } }
Biên dịch và chạy chương trình C# trên sẽ cho kết quả sau:
Quản lý Thread trong C#
Lớp Thread trong C# cung cấp các phương thức đa dạng để quản lý các Thread.
Ví dụ sau minh họa cách sử dụng phương thức sleep() để làm một Thread dừng trong một khoảng thời gian cụ thể.
using System; using System.Threading; namespace VietTutsCsharp { class Program { public static void CallToChildThread() { Console.WriteLine("Bat dau Thread con!!!"); // Thread nay dung khoang 5000 milisecond int sleepfor = 5000; Console.WriteLine("Thread con dung trong khoang {0} giay", sleepfor / 1000); Thread.Sleep(sleepfor); Console.WriteLine("Thread con phuc hoi!!!"); } static void Main(string[] args) { Console.WriteLine("Da luong trong C#"); Console.WriteLine("Vi du minh hoa quan ly Thread"); Console.WriteLine("---------------------------------"); ThreadStart childref = new ThreadStart(CallToChildThread); Console.WriteLine("Trong Main Thread: tao Thread con."); Thread childThread = new Thread(childref); childThread.Start(); Console.ReadKey(); } } }
Biên dịch và chạy chương trình C# trên sẽ cho kết quả sau:
Hủy Thread trong C#
Phương thức Abort() được sử dụng để hủy các Thread trong C#.
Trong thời gian runtime, chương trình hủy bỏ Thread bằng việc ném một ThreadAbortException. Exception này không thể được bắt, điều khiển được gửi tới khối finally, nếu là không.
Dưới đây là chương trình minh họa việc sử dụng phương thức Abort() để hủy bỏ các Thread trong C#:
using System; using System.Threading; namespace VietTutsCsharp { class Program { public static void CallToChildThread() { try { Console.WriteLine("Bat dau Thread con!!!"); // gia su chung ta dem tu 0 toi 10 for (int counter = 0; counter <= 10; counter++) { Thread.Sleep(500); //dung trong khoang 5 giay Console.WriteLine(counter); } Console.WriteLine("Thread con hoan thanh."); } catch (ThreadAbortException e) { Console.WriteLine("Thread Abort Exception!!!"); } finally { Console.WriteLine("Khong the bat Thread Exception!!!"); } } static void Main(string[] args) { Console.WriteLine("Da luong trong C#"); Console.WriteLine("Vi du minh hoa huy Thread"); Console.WriteLine("-------------------------------------"); ThreadStart childref = new ThreadStart(CallToChildThread); Console.WriteLine("Trong Main Thread: tao Thread con."); Thread childThread = new Thread(childref); childThread.Start(); //dung Main Thread trong khoang 2 giay Thread.Sleep(2000); //bay gio huy thread con Console.WriteLine("Trong Main Thread: huy Thread con."); childThread.Abort(); Console.ReadKey(); } } }
Biên dịch và chạy chương trình C# trên sẽ cho kết quả sau: