Giáo trình thực hành Lập trình hướng đối tượng - Bài thực hành số 9.4: Viết phần mềm giải quyết tương tranh giữa các thread

Trang 1  
MÔN : LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG  
Bài thực hành số 9.4 : Viết phần mềm giải quyết tương tranh giữa các thread  
I. Mục tiêu :  
ƒ
Giúp SV làm quen với việc dùng class Thread của namespace System.Threadings ₫ể quản  
lý thread.  
ƒ
Giúp SV làm quen với việc dùng class Mutex của namespace System.Threadings ₫ể loại  
trừ tương hỗ giữa các thread khi chúng cùng truy xuất vào tài nguyên dùng chung.  
II. Nội dung :  
ƒ
Xây dựng chương trình cho phép người dùng thực hiện quản lý các thread có tên là A-Z  
như kích hoạt chạy, tạm dừng, chạy lại, tăng/giảm quyền ưu tiền, dừng và xóa thread...  
bằng các thao tác ấn phím. Mỗi thread khi chạy sẽ hiển thị icon miêu tả mình lên form,  
icon sẽ chạy theo 1 phương xác ₫ịnh và khi ₫ụng thành form thì dội lại theo nguyên lý vật  
lý.  
ƒ
Dùng Mutex ₫ể quản lý từng cell hiển thị, thread nào muốn hiển thị cell nào thì phải gọi  
tác vụ WaitOne() của Mutex của cell tương ứng. Khi không dùng cell cũ nữa, thread phải  
gọi tác vụ ReleaseMutex() của Mutex của cell ₫ó.  
III. Chuẩn ₫ầu ra :  
ƒ
Sinh viên nắm vững và sử dụng thành thạo class Thread ₫ể quản lý thread.  
ƒ
Sinh viên nắm vững vấn ₫ề tương tranh giữa các thread khi chúng truy xuất tài nguyên  
dùng chung.  
ƒ
Sinh viên nắm vững và sử dụng thành thạo class Mutex ₫ể loại trừ tương hỗ giữa các  
thread khi chúng cùng truy xuất vào tài nguyên dùng chung.  
IV. Qui trình :  
1. Chạy VS .Net, chọn menu File.New.Project ₫ể hiển thị cửa sổ New Project.  
2. Mở rộng mục Visual C# trong TreeView "Project Types", chọn mục Windows, chọn icon  
"Windows Form Application" trong listbox "Templates" bên phải, thiết lập thư mục chứa  
Project trong listbox "Location", nhập tên Project vào textbox "Name:" (td. ThreadDemo3),  
click button OK ₫ể tạo Project theo các thông số ₫ã khai báo.  
3. Form ₫ầu tiên của ứng dụng ₫ã hiển thị trong cửa sổ thiết kế. Bài thực hành này không thiết  
kế form mà chỉ viết code cho chương trình vì form ₫ược hiệu chỉnh kích thước ₫ộng và nội  
dung hiển thị trong form cũng ₫ược hiểu chỉnh ₫ộng bởi các thread ₫ang chạy.  
4. Chọn Form, cửa sổ thuộc tính của nó sẽ hiển thị, click icon  
₫ể hiển thị danh sách các sự  
kiện của Form, duyệt tìm sự kiện Load, ấn kép chuột vào comboBox bên phải sự kiện Load ₫ể  
máy tạo tự ₫ộng hàm xử lý cho sự kiện này. Cửa sổ mã nguồn sẽ hiển thị khung sườn của hàm  
vừa ₫ược tạo với thân rỗng, viết thân cho hàm này như sau :  
private void Form1_Load(object sender, EventArgs e) {  
//tạo ₫ối tượng quản lý việc truy xuất tài nguyên trong project  
System.Reflection.Assembly myAssembly =  
System.Reflection.Assembly.GetExecutingAssembly();  
threadLst = new MyThread[26];  
int i;  
//tạo ma trận semaphore Mutex ₫ể bảo vệ các cell nàm hình  
mutList = new Mutex[yMax, xMax];  
int h, cot;  
for (h = 0; h < yMax; h++)  
for (cot = 0; cot < xMax; cot++)  
mutList[h, cot] = new Mutex();  
Trang 2  
//Lặp thiết lập trạng thái ban ₫ầu cho 26 thread từ A-Z  
for (i = 0; i < 26; i++)  
{
threadLst[i] = new MyThread(rnd, xMax, yMax);  
threadLst[i].stop = threadLst[i].suspend = threadLst[i].start = false;  
char c = (char)(i + 65);  
//₫ọc bitmap miêu tả thread c từ file  
myStream =  
myAssembly.GetManifestResourceStream("ThreadDemo3.Resources.Image" + c.ToString() +  
".bmp");  
threadLst[i].Pic = new Bitmap(myStream);  
threadLst[i].Xmax = 25;  
threadLst[i].Ymax = 20;  
}
ClientSize = new Size(25 * 30, 20 * 30);  
this.Location = new Point(0, 0);  
this.BackColor = Color.Black;  
}
5. Tạo hàm xử lý sự kiện KeyDown cho Form. Cửa sổ mã nguồn sẽ hiển thị khung sườn của hàm  
vừa ₫ược tạo với thân rỗng, viết thân cho hàm này như sau :  
//hàm xử lý gỏ phím của user ₫ể quản lý thread  
private void Form1_KeyDown(object sender, KeyEventArgs e) {  
//xác ₫ịnh mã phím ấn, nếu không phải từ A-Z thì phớt lờ  
int newch = e.KeyValue;  
if (newch < 0x41 || newch > 0x5a) return;  
//xác ₫ịnh chức năng mà user muốn và thực hiện  
if (e.Control && e.Shift)  
{ //kill thread  
// dừng Thread  
threadLst[newch - 65].start = false;  
}
else if (e.Control)  
{ //giảm ₫ộ ưu tiên tối thiểu  
threadLst[newch - 65].t.Priority = ThreadPriority.Lowest;  
MessageBox.Show(threadLst[newch - 65].t.Priority.ToString());  
}
else if (e.Control && e.Alt)  
{ //tạm dừng thread  
if (threadLst[newch - 65].start && !threadLst[newch - 65].suspend)  
{
threadLst[newch - 65].t.Suspend();  
threadLst[newch - 65].suspend = true;  
}
}
else if (e.Alt)  
{ //cho thread chạy lại  
if (threadLst[newch - 65].start && threadLst[newch - 65].suspend)  
{
threadLst[newch - 65].t.Resume();  
threadLst[newch - 65].suspend = false;  
}
}
Trang 3  
else if (e.Shift)  
{ //tăng ₫ộ ưu tiên tối ₫a  
threadLst[newch - 65].t.Priority = ThreadPriority.Highest;  
MessageBox.Show(threadLst[newch - 65].t.Priority.ToString());  
}
else  
{ //tạo mới thread và bắt ₫ầu chạy  
if (!threadLst[newch - 65].start)  
{
threadLst[newch - 65].start = true;  
threadLst[newch - 65].suspend = false;  
threadLst[newch - 65].t = new Thread(new ParameterizedThreadStart(Running));  
if (newch == 65) threadLst[newch - 65].t.Priority = ThreadPriority.Highest;  
else threadLst[newch - 65].t.Priority = ThreadPriority.Lowest;  
threadLst[newch - 65].t.Start(threadLst[newch - 65]);  
}
}
}
6. Tạo hàm xử lý sự kiện FormClosed cho Form. Cửa sổ mã nguồn sẽ hiển thị khung sườn của  
hàm vừa ₫ược tạo với thân rỗng, viết thân cho hàm này như sau :  
private void Form1_FormClosed(object sender, FormClosedEventArgs e) {  
int i;  
//lặp kiểm tra xem có thread con nào còn chạy không, nếu có thì xóa nó  
for (i = 0; i < 26; i++)  
if (threadLst[i].start) {  
threadLst[i].start = false;  
while (!threadLst[i].stop) ;  
}
}
7. Dời chuột về ₫ầu class Form1 rồi thêm lệnh ₫ịnh nghĩa các kiểu dữ liệu, các thuộc tính, các  
hàm dịch vụ cần dùng như sau :  
//₫ịnh nghĩa các thuộc tính cần dùng  
Stream myStream;  
Mutex[,] mutList;  
MyThread[] threadLst;  
const int xCell = 30;  
const int yCell = 30;  
const int xMax = 25;  
const int yMax = 20;  
//tạo ₫ối tượng sinh số ngẫu nhiên  
public Random rnd = new Random();  
//₫ịnh nghĩa hàm giả lập hành vi của thread  
void MySleep(long count)  
{
long i, j, k = 0;  
for (i = 0; i < count; i++)  
for (j = 0; j < 64000; j++) k = k + 1;  
}
//₫ịnh nghĩa hàm mà mỗi thread sẽ chạy  
Trang 4  
void Running(object obj)  
{
//ép kiểu tham số về MyThread theo yêu cầu xử lý  
MyThread p = (MyThread)obj;  
//tạo ₫ối tượng vẽ  
Graphics g = this.CreateGraphics();  
//tạo chổi màu ₫en ₫ể xóa cell cũ  
Brush brush = new SolidBrush(Color.FromArgb(0, 0, 0));  
//xin khóa truy xuất cell (x1,y1)  
mutList[p.Pos.Y, p.Pos.X].WaitOne();  
int x1, y1;  
int x2, y2;  
int x, y;  
bool kq=true;  
try  
{
while (p.start)  
{ //lặp trong khi chưa có yêu cầu kết thúc  
//xác ₫ịnh tọa ₫ộ hiện hành của thread  
x1 = p.Pos.X; y1 = p.Pos.Y;  
//hiển thị logo của thread ở (x1,y1)  
g.DrawImage(p.Pic, xCell * x1, yCell * y1);  
Color c = p.Pic.GetPixel(1,1);  
int yR, yG, yB;  
if (c.R > 128) yR = 0; else yR = 255;  
if (c.G > 128) yG = 0; else yG = 255;  
if (c.B > 128) yB = 0; else yB = 255;  
Pen pen = new Pen(Color.FromArgb(yR, yG, yB), 2);  
if (p.tx >= 0 && p.ty >= 0) { //hiện mũi tên góc dưới phải  
x = xCell * x1 + xCell - 2;  
y = yCell * y1 + yCell - 2;  
g.DrawLine(pen, x, y, x - 10, y);  
g.DrawLine(pen, x, y, x, y-10);  
} else if (p.tx >= 0 && p.ty < 0) { //hiện mũi tên góc trên phải  
x = xCell * x1 + xCell - 2;  
y = yCell * y1 + 2;  
g.DrawLine(pen, x, y, x - 10, y);  
g.DrawLine(pen, x, y, x, y + 10);  
} else if (p.tx < 0 && p.ty >= 0) { //hiện mũi tên góc dưới trái  
x = xCell * x1 + 2;  
y = yCell * y1 + yCell - 2;  
g.DrawLine(pen, x, y, x + 10, y);  
g.DrawLine(pen, x, y, x, y - 10);  
} else {//hiện mũi tên góc trên trái  
x = xCell * x1 + 2;  
y = yCell * y1 + 2;  
g.DrawLine(pen, x, y, x + 10, y);  
g.DrawLine(pen, x, y, x, y + 10);  
}
//giả lập thực hiện công việc của thread tốn 500ms  
MySleep(500);  
//xác ₫ịnh vị trí mới của thread  
Trang 5  
p.HieuchinhVitri();  
x2 = p.Pos.X; y2 = p.Pos.Y;  
//xin khóa truy xuất cell (x2,y2)  
while (true) {  
kq = mutList[y2, x2].WaitOne(new TimeSpan(0,0,2));  
if (kq==true || p.start==false) break;  
}
// Xóa vị trí cũ  
g.FillRectangle(brush, xCell * x1, yCell * y1, xCell, yCell);  
//trả cell (x1,y1) cho các thread khác truy xuất  
mutList[y1, x1].ReleaseMutex();  
if (kq == false && p.start == false)  
{ //xoa thread  
// dừng Thread  
p.t.Abort();  
p.stop = true;  
return;  
}
}
}
catch (Exception e) { p.t.Abort(); }  
//dọn dẹp thread trước khi ngừng  
x1 = p.Pos.X; y1 = p.Pos.Y;  
g.FillRectangle(brush, xCell * x1, yCell * y1, xCell, yCell);  
//trả cell (x1,y1) cho các thread khác truy xuất  
mutList[y1, x1].ReleaseMutex();  
// dừng Thread  
p.stop = true;  
p.t.Abort();  
}
8. Dời chuột về ₫ầu file mã nguồn Form1 rồi thêm lệnh using như sau :  
using System.Threading;  
using System.Resources;  
using System.IO;  
9. Ấn phải chuột vào phần tử gốc của cây Project trong cửa sổ Solution Explorer, chọn option  
Add.Class, ₫ặt tên là MyThread.cs ₫ể tạo ra file ₫ặc tả class chứa các tham số phục vụ cho  
từng thread con chạy. Khi cửa sổ hiển thị mã nguồn của class MyThread hiển thị, viết code  
cho class này như sau :  
class MyThread {  
const double PI = 3.1416;  
public Thread t;  
//tham khảo ₫ến thread hiện hành  
public Boolean start; //trạng thái Start của thread  
public Boolean stop; //trạng thái Start của thread  
public Boolean suspend; //trạng thái Suspend của thread  
public Boolean WaitOne = false; //trạng thái chờ truy xuất cell  
public Bitmap Pic;  
internal int Xmax;  
internal int Ymax;  
public Point Pos;  
double dblGocChay;  
//icon miêu tả thread  
//₫ộ rộng vùng chạy của thread  
//₫ộ cao vùng chạy của thread  
//vị trí của thread trong vùng chạy  
//góc chạy của thread  
internal double tx, ty; //bước tăng theo x và y  
//hàm khởi tạo các thông số của thread  
Trang 6  
public MyThread(Random rnd, int xMax, int yMax)  
{
Xmax = xMax; Ymax = yMax;  
Pos.X = (int)(rnd.Next(0, Xmax));  
Pos.Y = (int)(rnd.Next(0, Ymax));  
dblGocChay = ChinhGocChay(rnd.Next(0, 360));  
}
//================================================  
//Hiệu chỉnh góc chạy của thread  
//₫ể tránh các trường hợp thread chạy thẳng ₫ứng hay ngang  
//================================================  
double ChinhGocChay(double dblGocChay)  
{
double goc = dblGocChay;  
if (0 <= goc && goc < 90) return 45;  
if (90 <= goc && goc < 180) return 135;  
if (180 <= goc && goc < 270) return 225;  
if (270 <= goc) return 315;  
return goc;  
}
//================================================  
//Tính góc phản xạ mới khi thread ₫ụng thành ₫ứng (bên trái hay phải).  
//================================================  
double DoiGocChayX(double dblGocChay)  
{
double goc;  
if (dblGocChay > 0 && dblGocChay < 180) goc = 180 - dblGocChay;  
else goc = 180 + 360 - dblGocChay;  
return ChinhGocChay(goc);  
}
//================================================  
//Tính góc phản xạ mới khi thread ₫ụng thành ngang (trên hay dưới).  
//================================================  
double DoiGocChayY(double dblGocChay)  
{
return ChinhGocChay(360 - dblGocChay);  
}
//================================================  
//Hiệu chỉnh vị trí của thread  
//================================================  
public void HieuchinhVitri()  
{
int x, y;  
x = Pos.X;  
y = Pos.Y;  
if (x == 0 || x == Xmax - 1 || y == 0 || y == Ymax - 1)  
{
Trang 7  
//icon ₫ụng thành ngang hay dọc -> thay ₫ổi góc chạy  
if (x == 0 || x == Xmax - 1)  
{
dblGocChay = DoiGocChayX(dblGocChay);  
}
else if (y == 0 || y == Ymax - 1)  
dblGocChay = DoiGocChayY(dblGocChay);  
}
//Hiệu chỉnh tọa ₫ộ x của thread  
tx = 2 * Math.Cos(dblGocChay * PI / 180);  
x = x + (int)tx;  
if (x < 0)  
{
x = 0;  
}
else if (x >= Xmax)  
{
x = Xmax - 1;  
}
//Hiệu chỉnh tọa ₫ộ y của thread  
ty = 2 * Math.Sin(dblGocChay * PI / 180);  
y = y + (int)ty;  
if (y < 0)  
{
y = 0;  
}
else if (y >= Ymax)  
{
y = Ymax - 1;  
}
//chỉnh góc chạy khi ₫ụng 1 trong 4 góc  
if (x == 0 && y == 0) //góc trên trái  
ChinhGocChay(dblGocChay + 45);  
else if (x == 0 && y == Ymax - 1) //góc dưới trái  
ChinhGocChay(dblGocChay + 45);  
else if (x == Xmax - 1 && y == 0) //góc trên phải  
ChinhGocChay(dblGocChay + 45);  
else if (x == Xmax - 1 && y == Ymax - 1) //góc dưới phải  
ChinhGocChay(dblGocChay + 45);  
//Lưu vị trí mới  
Pos.X = (int)x;  
Pos.Y = (int)y;  
}
}
10. Dời chuột về ₫ầu file mã nguồn của class MyThread rồi thêm lệnh using như sau :  
using System.Threading;  
using System.Drawing;  
11. Ấn phải chuột vào phần tử gốc của cây Project trong cửa sổ Solution Explorer, chọn option  
Add.New Folder ₫ể thêm folder với tên là Resources, ta sẽ dùng folder này chứa các file  
bitmap ₫ược dùng trong chương trình.  
12. Ấn phải chuột vào folder Resources, chọn option Existing Items, duyệt chọn 26 file bitmap  
miêu tả 26 icon A-Z ₫ể add chúng vào folder Resources.  
Trang 8  
12. Chọn 26 mục vừa add vào folder Resources ₫ể hiển thị cửa sổ thuộc tính chúng của chúng,  
hiệu chỉnh lại thuộc tính Build Action về giá trị mới là "Embedded Resource".  
13. Chọn menu Debug.Start Debugging ₫ể dịch và chạy thử ứng dụng.  
14. Khi Form chương trình hiển thị, hãy thực hiện gỏ phím qui ₫ịnh như sau ₫ể quản lý các  
thread :  
ƒ Ấn phím từ A-Z ₫ể kích hoạt chạy thread có tên tương ứng.  
ƒ Ấn phím Ctrl-Alt-X ₫ể tạm dừng chạy thread X.  
ƒ Ấn phím Alt-X ₫ể chạy tiếp thread X.  
ƒ Ấn phím Shift-X ₫ể tăng ₫ộ ưu tiên chạy cho thread X.  
ƒ Ấn phím Ctrl-X ₫ể giảm ₫ộ ưu tiên chạy cho thread X.  
ƒ Ấn phím Ctrl-Shift-X ₫ể dừng và thoát thread X.  
Khi số thread chạy tương ₫ối nhiều, hãy quan sát hiện tượng icon của thread này tiến ₫ến và  
₫è icon của thread khác. Đây là vần ₫ề tương tranh giữa các thread khi chúng cùng truy xuất  
tài nguyên dùng chung (cell hiển thị).  
pdf 8 trang myanh 22160 Free
Bạn đang xem tài liệu "Giáo trình thực hành Lập trình hướng đối tượng - Bài thực hành số 9.4: Viết phần mềm giải quyết tương tranh giữa các thread", để 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:

  • pdfgiao_trinh_thuc_hanh_lap_trinh_huong_doi_tuong_bai_thuc_hanh.pdf