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
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:
bai_giang_lap_trinh_ung_dung_quan_ly_1_tuan_12_multithread_a.pdf

