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

Lập trình đa luồng trong windows

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 (237.2 KB, 42 trang )

Lập trình Windows
Multithread và Parallel programming
trong .NET


Mục tiêu cần nắm
Generic Types, Methods và Delegates
 Anonymous method
 Lambda Expression
 Multithread programming



Generic Types
 Generic nghĩa là “tổng quát” hàm ý đưa ra những cách làm

chung nhất cho nhiều vấn đề.
 Tình huống ví dụ cần xây dựng 1 Ngăn xếp (Stack) chứa kiểu
dữ liệu int với các phương thức cơ bản như: push, pop, kiểm
tra,… Sau đó lại cần xây dựng cũng Stack với các phương
thức như vậy nhưng dữ liệu lại là string.
 Giải pháp cho vấn đề trên?
public class IntStack
{
int[] _data = new int[100];
int _curIdx = -1;
public void Push(int value)
{
_data[++_curIdx] = value;
}
public int Pop()


{
return _data[_curIdx--];
}
}


Cách đầu tiên thường làm: copy lại và thay đổi kiểu
dữ liệu trong code. Sẽ tiếp tục như vậy nếu có yêu
cầu đổi kiểu dữ liệu sang 1 kiểu dữ liệu khác nữa
như float, double,…
 Cách tốt hơn khi đã biết đến kiểu dữ liệu cơ sở trong
.NET là object. Khi sử dụng kiểu dữ liệu object thì
Stack đã chấp nhận được mọi kiểu dữ liệu?


public class ObjectStack
{
object[] _data = new object[100];
int _curIdx = -1;
public void Push(object value)
{
_data[++_curIdx] = value;
}
public object Pop()
{
return _data[_curIdx--];
}
}





Vấn đề của ObjectStack kể trên là dễ gây ra lỗi
runtime (tức là chỉ giải quyết tốt về mặt syntax)
ObjectStack s = new ObjectStack();
//push vào kiểu string
s.Push("test");
//lấy ra lại kiểu int
int i = (int)s.Pop();



.NET cung cấp giải pháp cho vấn đề này thông
qua khái niệm Generic.


Generic là 1 tính năng mới trong .NET Framework
2.0 và và được tích hợp sâu trong Common
Language Runtime (CLR).
 Trong .NET Framework, Generic được giới thiệu là
khái niệm về các kiểu tham số và được dùng để
thiết kế class và phương thức nhằm để trì hoãn chỉ
ra kiểu dữ liệu cho đến khi lớp hoặc phương thức
được khai báo hoặc khởi tạo.
 Một trong những điểm nổi bật của kiểu Generic là
cho phép kiểm tra cú pháp trong lúc biên dịch. Có
thể sử dụng nhiều kiểu dữ liệu khác nhau với cùng
1 đoạn code (tương tự như khái niệm Template
trong C++)




 Bằng cách sử dụng tham số T là tham số chung, ta có

thể tạo 1 class duy nhất mà khi tham chiếu tới sẽ không
gặp bất kỳ lỗi nào xảy ra trong lúc runtime khi ép kiểu
hoặc boxing (chuyển giá trị từ value type sang reference
type).
public class GenericStack<T>
{
T[] _data = new T[100];
int _curIdx = -1;
public void Push(T value)
{
_data[++_curIdx] = value;
}
public T Pop()
{
return _data[_curIdx--];
}
}
//... sử dụng
GenericStack<string> s = new GenericStack<string>();


Generic Methods


Là các hàm thông dụng (cách hành xử chung cho
các kiểu dữ liệu) nên cài đặt theo kiểu Generic

(sử dụng các tham số đầu vào hay đầu ra là
generic)
static void Swap<T>(ref T lhs, ref T rhs)
{
T temp;
temp = lhs;
lhs = rhs;
rhs = temp;
}


Generic Delegates


Tương tự với method, delegate cũng có thể cài
đặt theo kiểu generic (nhằm làm tham số hàm
chung nhất cho một số trường hợp).
public delegate void MyDelegate<T>(T param);



Trong .NET được khai báo sẵn một số các
generic delegate như: Predicate<T>, Action<T>,
Comparison<T>,…












Predicate<T> được sử dụng để kiểm tra các giá trị
có thỏa mãn một điều kiện nào đó không và trả về
kiểu bool.
Action<T> sử dụng để thực hiện các hành động
với đối tượng mà ta truyền vào và không trả về giá
trị nào cả.
Comparison<T> dùng để so sánh hai đối tượng
cùng kiểu, thường sử dụng trong các trường hợp
sắp xếp.

Ví dụ thường gặp nhất là các phương thức tĩnh
được cung cấp trong class Array.




Ví dụ class Array
 Phương thức tìm kiếm của class Array
T[] Array.FindAll<T>(T[] array, Predicate<T> match)
 Phương thức duyệt mảng
void Array.ForEach<T>(T[] array, Action<T> action)
 Phương thức sắp xếp
void Array.Sort> (T[] array, Comparison<T> comparison)




Cách sử dụng


bool FuncPredicate(int value)
{
return value % 2 == 0;
}
StringBuilder sb = new StringBuilder();
void FuncAction(int value)
{

sb.AppendFormat("{0}, ", value);
}
int FuncComparison(int x, int y)
{
return x.CompareTo(y);
}
void funcTest()
{

int[] arrInt = new int[] { 3, 5, 7, 2, 10, 43, 12, 34 };
//sử dụng Predicate
int[] arrTemp = Array.FindAll<int>(arrInt, new Predicate<int>(FuncPredicate));
//sử dụng Action
Array.ForEach(arrInt, new Action<int>(FuncAction));
//sử dụng Comparison
Array.Sort(arrInt, new Comparison<int>(FuncComparison));

}



Anonymous Method
 Anonymous method (tạm dịch là phương thức vô danh) là

phương thức không có tên được khai báo với từ khóa
delegate.
 Anonymous method cho phép tạo ra các hành động cho
một delegate với cách viết inline.
 Cụ thể
delegate void DelTest(int value);
void funcTest()
{
DelTest dt = delegate(int value)
{
//...
};
}


 Áp dụng trong event
Load += delegate(object sender, EventArgs e)
{
MessageBox.Show("Load raised...");
};
 Áp dụng trong các Generic Delegate
StringBuilder sb = new StringBuilder();
void funcTest()
{
int[] arrInt = new int[] { 3, 5, 7, 2, 10, 43, 12, 34 };

//sử dụng Predicate
int[] arrTemp = Array.FindAll<int>(arrInt, delegate(int value){
return value % 2 == 0;
});
//sử dụng Action
Array.ForEach(arrInt, delegate(int value){
sb.AppendFormat("{0}, ", value);
});
//sử dụng Comparison
Array.Sort(arrInt, delegate(int x, int y){
return x.CompareTo(y);
});
}


Lambda Expression
 Có từ C# 3.0
 Có thể nói so với anonymous method, lambda

expression được coi là một sự cái tiến đáng giá từ
phiên bản C# 2.0 lên C# 3.0.
 Khi dùng anonymous method, ta có thể tạo các hàm
in-line nhằm hạn chế việc khai báo các hàm riêng lẻ
không cần thiết, giúp mã lệnh ngắn gọn hơn.
 Với lambda expression thì có thể viết ngắn gọn và dễ
dàng hơn nhờ việc cung cấp toán tử và cú pháp mới,
đồng thời thể hiện sự “thông minh” của compiler bằng
cách tự nhận diện kiểu của dữ liệu.
 Ngoài ra, đây còn là kĩ thuật để tạo ra các kiểu
expression tree.



 Dạng của Lambda Expression như sau:

(input parameters) => expression
 Dấu mở và đóng ngoặc là tùy chọn trong trong trường

hợp chỉ có 1 tham số, ngược lại nó là bắt buộc.
 Nếu có nhiều hơn 1 tham số thì chúng sẽ được phân
cách bằng dấu phẩy (,).
 Kiểu dữ liệu của các tham số có thể được khai báo tường
minh hoặc không. Nếu không khai báo, trình biên dịch sẽ
tự xác định kiểu, tuy nhiên trong một số trường hợp, cần
phải chỉ rõ kiểu dữ liệu.
//sẽ báo lỗi
s => s.Length;
//cần khai báo kiểu dữ liệu
(string s) => s.Length;


Trong lambda expression sử dụng toán tử =>,
mang ý nghĩa như là “đi đến”.
 Phía bên trái của toán tử là các tham số (nếu có),
bên phải là các biểu thức hay câu lệnh. Ví dụ:


//lambda expression
x => x * x;
//hàm tương đương
int f(int x)

{
return x * x;
}




Sử dụng trong Generic Delegate
StringBuilder sb = new StringBuilder();
void funcTest()
{
int[] arrInt = new int[] { 3, 5, 7, 2, 10, 43, 12, 34 };
//sử dụng Predicate
int[] arrTemp = Array.FindAll<int>(arrInt, value =>{
return value % 2 == 0;
});
//sử dụng Action
Array.ForEach(arrInt, value => {
sb.AppendFormat("{0}, ", value);
});
//sử dụng Comparison
Array.Sort(arrInt, (x, y) => {
return x.CompareTo(y);
});
}


Delegate Func<…>
Từ C# 3.0, cùng với việc ra đời của lambda
expression, Microsoft cung cấp cho ta một kiểu

delegate mới linh hoạt và tiện dụng hơn, tên của
kiểu delegate này là Func.
 Func cho phép khai báo và tạo ra các dạng
delegate với số lượng tham số và kiểu trả về
khác nhau, tương tự như khi tạo ra một phương
thức.
 Func được dùng chủ yếu để tạo và lưu trữ một
anonymous method ngắn gọn bằng lambda
expression và được sử dụng như những phương
thức thông thường.



 Cú pháp để sử dụng Func là viết các kiểu của tham

số và giá trị trả về vào cặp ngoặc ‘<>’, theo sau từ
khóa Func.
Func<T, T, ..., TResult>

 Trong đó T là các kiểu của tham số cần truyền vào và

TResult là kiểu của giá trị trả về.
 Lưu ý là Func yêu cầu ít nhất một tham số trong cặp
‘<>’, tức là phải có kiểu trả về. Không để đặt void hay để
một cặp ngoặc ‘<>’ rỗng khi dùng Func.
 Ví dụ

//ví dụ hàm tìm max 2 số
Func<int, int, int> findMax = (x, y) =>{
return (x > y) ? x : y;

};
MessageBox.Show(findMax(27, 43).ToString());


Multithread
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.
…


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 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.




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");
}


×