Tải bản đầy đủ (.pdf) (16 trang)

Tuan12 multithreading, task, async

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (606.35 KB, 16 trang )

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, hồn tồ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

tồ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 đó hồ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 });
t2.Join();
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) đã hồ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.IsBackground = true;
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){
lock (syncObj){
n++;
if (n > 0){
Thread.Sleep(2); Console.Write("{0}\t", n);
}
}
}
});
Thread t2 = new Thread(() => {
for (int i = 0; i < 100; ++i){
lock (syncObj){
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ụ
Task<int> taskGetNumber()
{
return Task<int>.Run(() => {
// tác vụ tiêu tốn nhiều thời gian

return 1;
});
}
async void DoIt()
{
int num = await taskGetNumber();
// thực hiện các việc khác trong khi chờ kết quả
}

16



×