Bài giảng Lập trình ứng dụng quản lý - Tuần 12: Multithread, Asynchronous Programing

11/18/2019  
Lập trình Ứng dụng quản lý  
Multithread, Asynchronous Programing  
Nội dung  
Callback function  
Khái niệm MultiThread  
Asynchronous Programing  
1
11/18/2019  
Nội dung  
Callback function  
Khái niệm MultiThread  
Asynchronous Programing  
Khái niệm Callback function  
Trong lập trình máy tính, callback là một đoạn  
code chạy được (thường là một hàm A) được sử  
dụng như tham số truyền vào của hàm B nào đó.  
Tới một thời điểm nào đó, Hàm A sẽ được hàm B  
gọi lại (callback). Các ngôn ngữ lập trình khác  
nhau hỗ trợ callback theo cá c cá ch khá c nhau,  
thường được triển khai dưới dạng chương trình  
con, hàm nặc danh, chuỗi lệnh hoặc con trỏ hàm.  
2
11/18/2019  
Callback trong .NET  
Trong .NET thì sử dụng delegate để thực hiện  
callback.  
Ví dụ:  
static void FindPrimes(int min, Action<int> callback)  
{
for (int i = min; i < int.MaxValue; i++)  
{
// kiểm tra i có phải là Prime ko?  
callback(i);  
}
}
Event bản chất cũng là callback.  
Nội dung  
Callback function  
Khái niệm MultiThread  
Asynchronous Programing  
3
11/18/2019  
Multithreading  
Má y tí nh ngày nay thực hiện được đa nhiệm, đa  
tá c vụ đồng thời dựa vào khá i niệm Process và  
Thread.  
Process (tiến trì nh) có thể hiểu là một thể hiện  
(instance) của chương trì nh má y tí nh được thực  
thi, dựa trên hệ điều hành, hoàn toàn độc lập với  
cá c tiến trì nh khá c.  
Thread là một nhó m lệnh được tạo ra để thực thi  
một tá c vụ trong một process, chúng chia sẻ  
chung dữ liệu với nhau để xử lý, điều này là cần  
thiết nhưng cũng là nguyên nhâ n dễ gâ y ra lỗi  
nếu khô ng được xử lý đúng cá ch.  
Ngữ cảnh sử dụng  
Tương tác với giao diện trong khi các tác vụ  
ngầm vẫn chạy  
Lướt web trong khi Chrome đồng bộ bookmark  
Thiết lập độ ưu tiên  
Hoạt động tiêu tốn nhiều thời gian không dừng  
toàn bộ ứng dụng  
Copy file dung lượng lớn!  
Import file vào CSDL.  
…  
4
11/18/2019  
Namespace System.Threading  
Cung cấp lớp và giao diện cho lập trình đa luồng  
Class chí nh: Thread  
CurrentThread  
IsAlive  
IsBackground  
Name  
Priority  
ThreadState  
Tạo một Thread  
Tạo phương thức (gọi là phương thức callback)  
sẽ thực thi khi thread được gọi.  
Phương thức này phải không có tham số hoặc chỉ  
có một tham số là kiểu object và kiểu trả về là void.  
Bước này có thể bỏ qua vì ta có thể sử dụng  
anonymous method hoặc lambda expression để tạo  
đoạn mã lệnh thực thi in-line cùng với lệnh khởi tạo  
thread.  
Tạo đối tượng Thread và truyền một delegate  
ThreadStart chứa phương thức sẽ thực thi vào  
constructor của Thread.  
Chạy thread: Gọi phương thức Start() của đối  
tượng thread vừa tạo.  
5
11/18/2019  
Ví dụ  
static void Main(string[] args)  
{
Thread t1 = new Thread(new ThreadStart(MethodA));  
Thread t2 = new Thread(new ThreadStart(MethodB));  
t1.Start();  
t2.Start();  
}
static void MethodA()  
{
for (int i = 0; i < 100; ++i)  
Console.Write("A");  
}
static void MethodB()  
{
for (int i = 0; i < 100; ++i)  
Console.Write("B");  
}
Sử dụng lambda expression  
static void Main(string[] args)  
{
Thread t1 = new Thread(() => {  
for (int i = 0; i < 100; ++i)  
Console.Write("A");  
});  
Thread t2 = new Thread(() => {  
for (int i = 0; i < 100; ++i)  
Console.Write("B");  
});  
t1.Start();  
t2.Start();  
}
6
11/18/2019  
Truyền tham số cho Thread  
ParameteriedThreadStart là một giải pháp thay  
thế cho ThreadStart trong trường hợp cần truyền  
tham số cho thread.  
Đối tượng delegate ParameteriedThreadStart này  
chỉ chấp nhận một tham số kiểu object, vì thế  
trong phương thức callback cần phải ép kiểu để  
sử dụng được đúng kiểu dữ liệu của tham số.  
Ví dụ  
static void Main(string[] args)  
{
Thread t1 = new Thread(new ParameterizedThreadStart(Method));  
Thread t2 = new Thread(new ParameterizedThreadStart(Method));  
t1.Start("A");  
t2.Start("B");  
}
static void Method(object param)  
{
string str = (string)param;  
for (int i = 0; i < 100; ++i)  
Console.Write(str);  
}
7
11/18/2019  
Sử dụng lambda expression  
static void Main(string[] args)  
{
Thread t1 = new Thread((param) => {  
string str = (string)param;  
for (int i = 0; i < 100; ++i)  
Console.Write(str);  
});  
Thread t2 = new Thread((param) =>  
{
string str = (string)param;  
for (int i = 0; i < 100; ++i)  
Console.Write(str);  
});  
t1.Start("A");  
t2.Start("B");  
}
Property quan trọng của Thread  
ThreadState cho thấy trạng thái hiện tại của  
thread. Mỗi một lời gọi phương thức của thread  
sẽ làm thay đổi giá trị thuộc tính này như  
Unstarted, Running, Suspended, Stopped,  
Aborted,….  
ThreadPriority xác định mức độ ưu tiên mà  
thread sẽ được thực thi so với các thread khác.  
Mỗi thread khi được tạo ra mang giá trị priority là  
Normal. Các giá trị mà thuộc tính có thể có bao  
gồm: Lowest, BelowNormal, Normal,  
AboveNormal và Highest.  
8
11/18/2019  
Các phương thức thông dụng của  
Thread  
Abort(): khi phương thức này được gọi, hệ  
thống sẽ ném ra một ngoại lệ  
ThreadAbortException để kết thúc thread. Sau  
khi gọi phương thức này, thuộc tính ThreadState  
sẽ chuyển sang giá trị Stopped.  
Suspend(): phương thức này sẽ tạm dừng việc  
thực thi của Thread vô thời hạn cho đến khi nó  
được yêu cầu chạy tiếp tục với phương thức  
Resume(). Tuy nhiên hai phương thức này được  
gắn attribute Obsolete để khuyến cáo rằng ta  
nên sử dụng những phương pháp khác để thay  
thế.  
Sleep(): để dừng thread hiện tại trong một  
khoảng thời gian tính bằng milisecond, khi đó  
thread sẽ chuyển sang trạng thái WaitSleepJoin.  
Chú ý rằng đây là một phương thức static do đó  
không cần tạo đối tượng Thread khi gọi nó, ví dụ:  
Thread.Sleep(1000). Tùy vào ngữ cảnh gọi  
Thread.Sleep(), mà Thread thực thi dòng lệnh  
này sẽ bị ảnh hưởng.  
Join(): đây là một phương thức hữu ích trong  
trường hợp cần thực hiện một tác vụ nào đó sau  
khi thread đã kết thúc. Phương thức này chỉ  
được dùng sau khi đã chạy Thread. Các tác vụ  
nằm phía dưới lệnh gọi Join() của một Thread chỉ  
được thực thi sau khi Thread đó hoàn tất công  
việc của mình.  
9
11/18/2019  
Ví dụ  
static void Main(string[] args)  
{
Thread t1 = new Thread(new ParameterizedThreadStart(Method));  
Thread t2 = new Thread(new ParameterizedThreadStart(Method));  
Thread t3 = new Thread(new ParameterizedThreadStart(Method));  
t1.Start(new object[] { "A", 0 });  
t2.Start(new object[] { "B", 100 });  
t3.Start(new object[] { "C", 0 });  
}
static void Method(object param)  
{
object[] arrObj = (object[])param;  
string str = (string)arrObj[0];  
int n = (int)arrObj[1];  
Thread.Sleep(n);  
for (int i = 0; i < 100; ++i)  
Console.Write(str);  
}
Kết quả là C chỉ xuất hiện sau B dù có làm chậm Thread 2 đi.  
Foreground và Background Thread  
Foreground Thread là những Thread sẽ được  
thực thi cho đến khi kết thúc mặc dù chương  
trình (Thread chính) đã hoàn thành (hoặc bị bắt  
buộc ngừng) và kết thúc.  
Background Thread thì sẽ bị khai tử ngay khi  
chương trình kết thúc.  
Mặc định các Thread khi tạo ra là Foreground.  
Thuộc tính để set Foreground hay Background là  
IsBackground.  
10  
11/18/2019  
Ví dụ  
static void Main(string[] args)  
{
Thread t1 = new Thread(() => {  
Thread.Sleep(1000);  
Console.WriteLine("Thread 1 running...");  
});  
Thread t2 = new Thread(() => {  
Thread.Sleep(1200);  
Console.WriteLine("Thread 2 running...");  
});  
t1.Start();  
t2.Start();  
Console.WriteLine("Main ended...");  
}
Thread Pooling  
Thread Pooling là một kĩ thuật cho phép sử  
dụng các thread hiệu quả hơn bằng cách quản lý  
và phân phối chúng hợp lý, tận dụng tối đa thời  
gian nhàn rỗi và tăng hiệu suất của chương trình.  
Thread Pooling là một kĩ thuật được áp dụng  
phổ biến trong các ứng dụng về I/O bất đồng bộ  
tập tin và truyền tải dữ liệu trên mạng.  
Một đặc điểm của Thread pool là các thread sẽ  
được đặt ở chế độ background (Background  
Thread).  
Để sử dụng thread pool thì chỉ đơn giản sử dụng  
phương thức tĩnh QueueUserWorkItem() của lớp  
ThreadPool.  
11  
11/18/2019  
Ví dụ  
static void Main(string[] args)  
{
ThreadPool.QueueUserWorkItem(funcWait, "A");  
ThreadPool.QueueUserWorkItem(funcWait, "B");  
Thread.Sleep(6000);  
}
static void funcWait(object param)  
{
string str = (string) param;  
for (int i = 0; i < 10; ++i)  
{
Console.WriteLine("{0} running...", str);  
Thread.Sleep(500);  
}
}
Các vấn đề về đụng độ  
Vấn đề nảy sinh khi các Thread cùng truy cập đến một đối tượng, trạng thái chia  
sẻ bất đồng bộ.  
Ví dụ chương trình sau  
static int n = 0;  
static void Main(string[] args)  
{
Thread t1 = new Thread(() => {  
for (int i = 0; i < 100; ++i){  
n++;  
if (n > 0){  
Thread.Sleep(2); Console.Write("{0}\t", n);  
}
}
});  
Thread t2 = new Thread(() => {  
for (int i = 0; i < 100; ++i){  
Thread.Sleep(1); n--;  
}
});  
t1.Start(); t2.Start();  
}
12  
11/18/2019  
Giải quyết vấn đề bằng đồng bộ  
.Net cung cấp một số kĩ thuật để đồng bộ việc  
truy xuất dữ liệu. Một khi được sử dụng, dữ liệu  
sẽ bị khóa lại và các thread khác muốn sử dụng  
phải chờ cho đến khi dữ liệu hay tài nguyên được  
giải phóng.  
Cụ thể các phương pháp được cung cấp là:  
Monitor, SpinLock, Mutex (Mutual exclusive),  
Semaphore, WaitHandle,...  
Đơn giản nhất là sử dụng Lock.  
Ví dụ  
static int n = 0;  
static object syncObj = new object();  
static void Main(string[] args)  
{
Thread t1 = new Thread(() => {  
for (int i = 0; i < 100; ++i){  
n++;  
if (n > 0){  
Thread.Sleep(2); Console.Write("{0}\t", n);  
}
}
}
});  
Thread t2 = new Thread(() => {  
for (int i = 0; i < 100; ++i){  
Thread.Sleep(1); n--;  
}
}
});  
t1.Start(); t2.Start();  
}
13  
11/18/2019  
Deadlock  
Đồng bộ hóa khi sử dụng thread là một công việc  
cần thiết, tuy nhiên nếu không cẩn thận thì sẽ  
gặp phải tình trạng chương trình dừng hoạt động  
vô thời hạn. Tình trạng này được đặt tên là  
Deadlock.  
Deadlock xảy ra khi có ít nhất hai thread cùng  
đợi thread kia giải phóng, thật “trùng hợp” là cả  
hai lại đang giữ “chìa khóa” của nhau.  
Ví dụ  
static int m = 0, n = 0;  
static object syncM = new object();  
static object syncN = new object();  
static void Main(string[] args)  
{
Thread t1 = new Thread(() => {  
lock (syncM){  
m++; Thread.Sleep(100); Console.WriteLine("t1 holding m");  
lock(syncN){  
n--; Console.WriteLine("t1 holding n");  
}
}
});  
Thread t2 = new Thread(() => {  
lock (syncN)  
{
n++; Thread.Sleep(100); Console.WriteLine("t2 holding n");  
lock (syncM)  
{
m--; Console.WriteLine("t2 holding m");  
}
}
});  
t1.Start(); t2.Start();  
}
14  
11/18/2019  
Nội dung  
Callback function  
Khái niệm MultiThread  
Asynchronous Programing  
Asynchronous Programing  
Là cơ chế xử lý đồng thời nhiều request bằng các  
thread khác nhau trong khi đợi kết quả từ các tác  
vụ tiêu tốn tài nguyên lớn như I/O, access  
database,…  
Lợi ích đạt được là:  
Trải nghiệm người dùng (usability).  
Hiệu năng (performance).  
15  
11/18/2019  
BeginInvoke và EndInvoke  
Delegate .NET có 2 phương thức giúp thực hiện  
Asynchronous Programing là:  
BeginInvoke (args, AsyncCallback, @object) trả về  
IAsyncResult: tự động tạo thread mới và thực hiện  
tác vụ trong thread mới này.  
EndInvoke (IAsyncResult) trả về kết quả của tác vụ  
thực hiện trong delegate.  
Task, Async, Await  
Từ .NET 4.5 đưa ra framework mới để thực hiện  
Asynchronous Programing.  
Một phương thức để thực thi asynchronous thì  
cần sử dụng kiểu trả về là Task.  
Ví dụ  
taskGetNumber()  
{
return (() => {  
// tác vụ tiêu tốn nhiều thời gian  
return 1;  
});  
}
void DoIt()  
{
int num = taskGetNumber();  
// thực hiện các việc khác trong khi chờ kết quả  
}
16  
pdf 16 trang myanh 27640 Free
Bạn đang xem tài liệu "Bài giảng Lập trình ứng dụng quản lý - Tuần 12: Multithread, Asynchronous Programing", để tải tài liệu gốc về máy hãy click vào nút Download ở trên

File đính kèm:

  • pdfbai_giang_lap_trinh_ung_dung_quan_ly_1_tuan_12_multithread_a.pdf