Đồng bộ Thread sử dụng Lock trong c#

Tại sao cần bảo vệ các Share Resource trong lập trình multithreading ?
- Nếu chúng ta sử dụng một shared resource và có nhiều luồng đồng thời truy cập vào nó, nếu không bảo vệ nó khỏi các truy cập đồng thời thì chúng ta sẽ nhận được các đầu ra kết quả không nhất quán.
- Trong C#, chúng ta có thể dùng Lock và Monitor có trong lớp Thread Safety, nó đảm bảo cơ chế chỉ một Thread thực thi phần mã quan trọng tại mỗi thời điểm bất kỳ
- Trong bài viết này, sẽ đề cập đến việc sử dụng Lock
Truy cập shared resource bằng Single Thread
Xem ví dụ sau đây: tạo một hàm sharedResource và gọi trong hàm Main() 3 lần
internal class Program
{

    static void Main()
    {
        SharedResource();
        SharedResource();
        SharedResource();

        Console.ReadKey();
    }

    static void SharedResource()
    {
        Console.WriteLine("This is my resource !");
        Thread.Sleep(1000);
        Console.WriteLine("Read Resource !\n");
    }
}
Kết quả


Trường hợp tạo các thread khác nhau để truy cập vào hàm SharedResource
 internal class Program
 {
     static readonly Thread _thread1 = new(SharedResource);
     static readonly Thread _thread2 = new(SharedResource);
     static readonly Thread _thread3 = new(SharedResource);
     static void Main()
     {
         _thread1.Start();
         _thread2.Start();
         _thread3.Start();

         Console.ReadKey();
     }

     static void SharedResource()
     {
         Console.WriteLine("This is my resource !");
         Thread.Sleep(1000);
         Console.WriteLine("Read Resource !\n");
     }
 }
 
Kết quả:
This is my resource !
This is my resource !
This is my resource !
Read Resource !
Read Resource !
Read Resource !

Ở code trên ta thấy cả 3 thread đều start và truy cập đến hàm SharedResource, do hàm có delay 1000ms nên thread thứ 1 start, sau đó thread 2, 3 cũng start vào, Lúc này cả 3 đều in ra dòng "This is my resource !" rồi sau đó mới in ra các thông tin còn lại. 
Do đó ta có thể thấy được thứ tự không dược quản lý tuần tự và in ra nội dung mong muốn như khi dùng single thread, trong lập trình bất động bộ thì hạn chế trường hợp này xảy ra vì rất khó quản lý được nội dung truy cập bởi nhiều thread

Cách giải quyết ?
Để giải quyết vấn đề nhiều thread truy cập vào một resource ta có thể dùng lock

 internal class Program
 {
     static readonly Thread _thread1 = new(SharedResource);
     static readonly Thread _thread2 = new(SharedResource);
     static readonly Thread _thread3 = new(SharedResource);

     static readonly object _lockAccess = new(); // create lock object

     static void Main()
     {
         _thread1.Start();
         _thread2.Start();
         _thread3.Start();

         Console.ReadKey();
     }

     static void SharedResource()
     {
         lock(_lockAccess)
         {
             Console.WriteLine("This is my resource !");
             Thread.Sleep(1000);
             Console.WriteLine("Read Resource !\n");
         }
     }
 }
 
Khi một khoá được giữ, thread đang giữ khoá có thể đạt được và giải phóng khoá đó. Các thread khác sẽ bị chặn lấy khoá và phải chờ đến khi khoá được giải phóng mới có thể lấy khoá
Lưu ý: tránh dùng một object khoá cho nhiều sharedresource vì nó có để dẫn đến deadlock
Ví dụ thêm về Lock

namespace CsharpProgramming
{
    internal class Program
    {
        static readonly Thread _thread1 = new(IncrementCount);
        static readonly Thread _thread2 = new(IncrementCount);
        static readonly Thread _thread3 = new(IncrementCount);

        static readonly object _lockAccess = new(); // create lock object
        static int Count = 0;

        static void Main()
        {
            // Start các thread
            _thread1.Start();
            _thread2.Start();
            _thread3.Start();

            // Chờ tất cả thread hoàn thành
            _thread1.Join();
            _thread2.Join();
            _thread3.Join();

            Console.WriteLine(Count);
            Console.ReadKey();
        }

        static void IncrementCount()
        {
            for (int i = 1; i <= 1000000; i++)
            {
                lock (_lockAccess)
                {
                    Count++;
                }
            }
        }
    }
}
 
Ở đoạn code trên 3 thread đều dùng cùng một hàm IncrementCount để tăng biến Count lên. Lock object làm cho thread 1 thực thi đếm lên giá trị Count mà không bị các thread khác truy cập vào làm tăng biến Count một cách mất kiểm soát. Nên đoạn code trên ta có một kết quả là 3000000. Còn nếu không dùng Lock thì mỗi lần chạy thì sẽ cho một kết quả khác nhau

Post a Comment

Previous Post Next Post