1. Trang chủ
  2. » Luận Văn - Báo Cáo

bài thuyết trình thread trong c

17 0 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Nó có thể bao gồm nhiều "thread" và có không gian bộ nhớ riêng.Thread: Một thread hay còn gọi là luồng, trong bài thuyết trình, nhóm sẽ dùng từ thread và luồng luân phiên nhau, đây là mộ

Trang 1

ĐẠI HỌC KINH TẾ THÀNH PHỐ HỒ CHÍ MINH

MÔN PHÁT TRIỂN ỨNG DỤNG DESKTOP

Bài Thuyết TrìnhThread trong C#Giảng viên: THS.Nguyễn Mạnh TuấnThành viên: Phạm Thành Đạt

Lương Phúc Thiên ÂnNguyễn Hoàng HuyThạch Quốc HuyTrịnh Liên HưngVũ Trọng Tiến Phi

Từ Thiên Giao Bảo

SE001-K48TP Hồ Chí Minh 3/2023

Trang 2

MỤC LỤC

1.Giới thiệu 3

1.1. Các khái niệm cần biết 3

1.2. Giới thiệu Multi Thread trong C# 4

2.Thread trong C# 6

2.1. Sử dụng Thread trong C# 6

2.2. Foreground và background thread 9

2.3. Thread Pool, ThreadPool Thread 11

3.Thread trong C# Winform 12

3.1. Tạo thread 12

3.2. Thread-safe 13

4.Hạn chế của multi-threading 16

Trang 3

1 Giới thiệu

1.1 Các khái niệm cần biết

Process: Một process hay còn gọi là tiến trình, trong bài thuyết trình, nhóm sẽ dùng từ process và tiến trình luân phiên nhau, đây là một chương trình đang thực thi trong hệ điều hành Nó có thể bao gồm nhiều "thread" và có không gian bộ nhớ riêng.

Thread: Một thread hay còn gọi là luồng, trong bài thuyết trình, nhóm sẽ dùng từ thread và luồng luân phiên nhau, đây là một đơn vị nhỏ nhất của một tiến trình Nó chia sẻ không gian bộ nhớ với các thread khác trong cùng một process.

Callback: hàm là tham số của một hàm

public delegate void WorkCompletedCallBack(string result); public void DoWork(WorkCompletedCallBack callback) {

callback("Hello world"); }

public void Test()

Trang 4

1.2 Giới thiệu Multi Thread trong C#

1.2.1 Command Language Runtime

Trong NET Core, Common Language Runtime (CLR) đóng một vai tròquan trọng trong việc tạo và quản lý vòng đời của thread Trong một ứngdụng NET Core mới, CLR tạo ra một forethread duy nhất để thực thi code củachương trình thông qua phương thức Main Luồng này được gọi là luồng chính(main thread) Cùng với luồng chính này, một process có thể tạo một hoặc nhiềuluồng để thực thi một phần của code Một chương trình cũng có thể sử dụng lớpThreadPool để thực thi mã trên các worker threads mà CLR quản lý.

Theo mặc định, một chương trình C# là đơn luồng Điều đó có nghĩa làcode được thực thi theo một đường duy nhất từ trên xuống dưới từ trái sang phảitại một thời điểm bởi luồng chính Điểm vào của chương trình C# bắt đầu từphương thức Main, là đường dẫn của luồng chính.

1.2.2 Tại sao phải dùng Multithread?

Để có cái nhìn tổng quát về công dụng và cách sử dụng Thread trong lậptrình nói chung C# nói riêng, ta sẽ xem xét đoạn code tiếp đây.

Ta có 2 tác vụ Tác vụ thứ nhất là hàm DoSomeHeavyLifting tổng thờigian thực hiện là 4 giây và tác vụ thứ 2 là DoSomething với tổng thời gian thựchiện là không đáng kể.

public static void DoSomeHeavyLifting(){

Console.WriteLine("I'm lifting a truck!!");Thread.Sleep(1000);

Console.WriteLine("Tired! Need a 3 sec nap.");Thread.Sleep(1000);

Too long to read onyour phone? Save

to read later onyour computer

Save to a Studylist

Trang 5

Console.WriteLine("I'm awake.");}

public static void DoSomething(){

Console.WriteLine("Hey! DoSomething here!");for (int i = 0; i < 20; i++)

Console.Write($"{i} ");Console.WriteLine();

Console.WriteLine("I'm done.");}

Giả sử, ta thực hiện hàm DoSomeHeavyLifting trước thì hàmDoSomeThing sẽ phải chờ 4 giây cho tới khi hàm DoSomeHeavyLifting thựchiện xong Hãy thử tưởng tượng, nếu như bạn đang tải một file nào đó từ trangweb tốn hết 4 giây và trong thời gian đó, bạn không thể thao tác trên trang webđó nữa thì sẽ thế nào?

Multiple threads hay multithreading là giải pháp cho vấn đề này Đaluồng, hay đơn giản chỉ là việc sử dụng luồng, cho phép chúng ta tạo ra cácluồng phụ có thể được sử dụng để thực hiện các nhiệm vụ chạy ở nền mà khônglàm ảnh hưởng đến luồng chính của chương trình Điều này giúp ứng dụng trởnên linh hoạt và thân thiện với người dùng hơn, vì luồng chính vẫn có sẵn để xửlý chương trình chính mà không bị gián đoạn.

Nói một cách đơn giản, Multi thread là kỹ thuật sử dụng để thực hiệnnhiều tác vụ cùng một lúc.

Từ đó, MultiThreading mang lại nhiều lợi ích mà ta có thể kể đến 4 lợiích chính là:

Khả năng đáp ứng Khả năng chia sẻ tài nguyênTiết kiệm

Trang 6

Thread backgroundThread = new Thread(newThreadStart(DoSomeHeavyLifting));

Console.WriteLine("Main thread ends here.");Console.ReadKey();

Phương thức Thread.Start() khởi động một luồng mới Luồng mới nàyđược gọi là luồng worker hoặc luồng phụ Dù phương thức của luồng workermất bao lâu, mã của luồng chính vẫn được thực thi song song.

2 Thread trong C#

2.1 Sử dụng Thread trong C#

2.1.1 Tạo và thực thi Thread

Tạo đối tượng Thread và truyền một delegate ThreadStart chứa phươngthức sẽ thực thi vào constructor của Thread.

Một cách ngắn gọn hơn, thay vì truyền một đối tượng ThreadStart bạn cóthể truyền trực tiếp tên phương thức cần thực thi cho constructor của Thread.Trình biên dịch sẽ tự động tạo ra đối tượng ThreadStart dựa vào phương thứcmà bạn truyền vào:

Thread t = new Thread(MethodA);

Lambda expression là một phương pháp hữu ích để viết trực tiếp mã lệnhcần thực thi mà không phải tách riêng ra thành phương thức:

Thread t = new Thread(()=>{

Thực thi thread: Gọi phương thức Start() của đối tượng thread vừa tạoVí dụ:

class Program{

static void Main(){

Thread t = new Thread(newThreadStart(MethodA));

t.Start();MethodB(); Console.ReadLine();

static void MethodA(){

Trang 7

for (int i = 0; i < 100; i++)Console.Write("0");}

static void MethodB(){

for (int i = 0; i < 100; i++)Console.Write("1");}

Press any key to continue

Nếu bạn không dùng thread chạy lần lượt 2 phương thức MethodA() vàMethodB() thì kết quả in ra sẽ là 100 kí tự ‘1’ và sau đó là 100 ký tự ‘0’ Tuynhiên như bạn thấy khi dùng thread như ví dụ trên, kết quả in ra sẽ là mộtchuỗi lẫn lộn ‘1’ và ‘0’, tức là hai phương thức này chạy “đồng thời” vớinhau.

2.1.2 Truyền tham số trong Thread

ParameteriedThreadStart là một giải pháp thay thế cho ThreadStart trongtrường hợp bạn muốn truyền tham số cho thread Đối tượng delegateParameterizedThreadStart này chỉ chấp nhận một tham số kiểu object, vì thếtrong phương thức callback, bạn cần phải ép kiểu để sử dụng được đúng kiểu dữliệu của tham số.

Ví dụ:

namespace ThreadExample{

class Student{

public string Name { get; set; }public DateTime BirthDay { get; set; }}

class Program{

static void Main(){

Thread t1 = new Thread(Print);t1.Start(new Student() {Name = "Yin",

Trang 8

BirthDay = new DateTime(1989, 10,17) });

static void Print(object obj){

Student st = (Student)obj;Console.Write(st.Name + "\t" +

Yin 17/10/1989

2.1.3 ThreadPriority và ThreadState

ThreadState:cho thấy trạng thái hiện tại của thread Mỗi khi gọi phươngthức của thread sẽ làm thay đổi giá trị thuộc tính này như Unstarted, Running,Suspended, Stopped, Aborted,….

Ví dụ:

using System;

using System.Threading;namespace assss{

class Program{

static void Main(string[] args){

Thread thread = new Thread(ThreadMethod);Console.WriteLine(

$"Thread State:{thread.ThreadState}");thread.Start();

$"Thread State:{thread.ThreadState}");thread.Join();

Trang 9

Thread State: UnstartedThread State: Running

Thread State: Stopped

Thuộc tính 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 tạo ra mang giá trị priority làNormal Các giá trị mà thuộc tính có thể bao gồm: owest, BelowNormal,L

Normal, AboveNormal và Highest.

2.1.4 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ộtngoại lệ ThreadAbortException để kết thúc thread Sau khi gọiphươ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 Threadvô thời hạn cho đến khi nó được yêu cầu chạy tiếp tục với phươngthức Resume().

Sleep(): để dừng thread hiện tại trong một khoảng thời gian tínhbằng milisecond, khi đó thread sẽ chuyển sang trạng tháiWaitSleepJoin.

Ví dụ:

2.2 Foreground và background thread

Trong C#, luồng thực thi chương trình được chia thành hai loại làbackground (chạy ngầm) và foreground (thông thường) Sau khi luồngchính (main thread) thoát khỏi chương trình, luồng foreground sẽ tiếp tụcchạy đến khi công việc của nó hoàn tất Mọi luồng background sẽ “chết”khi không còn luồng foreground nào đang chạy.

Theo mặc định, luồng chính của chương trình và các luồng đượckhởi tạo qua class Thread là luồng foreground, còn các luồng thuộcthread pool hoặc đi vào chương trình qua unmanaged code là luồng

Trang 10

background Thuộc tính Thread.IsBackground xác định xem một luồngtrong chương trình thuộc loại background hoặc foreground

Code cài đặt chương trình:

using System;

using System.Threading;namespace ForeBack{

class Program{

static void ForegroundTask(){

Console.WriteLine("Foreground thread finishes.");

static void Main(){

Thread foregroundThread =new Thread(ForegroundTask);

foregroundThread.IsBackground = false;foregroundThread.Start();

Thread backgroundThread =new Thread(BackgroundTask);

backgroundThread.IsBackground = true;backgroundThread.Start();

Console.WriteLine("Main thread exits.");}

Main thread exits.

Ở đây hai luồng trên đều là background nên sẽ kết thúc sau khiluồng chính của chương trình đóng lại Cuối cùng, ta đặt thuộc tínhIsBackgroundcủa hai luồng thành giá trị false, output sẽ là:

Main thread exits.Foreground thread finishes.Background thread finishes.

Trang 11

Vì hai luồng giờ đã trở thành foreground nên chương trình sẽ chờ đến khichúng hoàn tất công việc rồi mới kết thúc.

2.3 Thread Pool, ThreadPool Thread

2.3.1 Thread Pool

Việc tạo và hủy luồng mới cần một khoảng chi phí về resources ảnhhưởng đến hiệu suất của ứng dụng Luồng cũng có thể bị chặn hoặcchuyển sang trạng thái chờ hoặc các trạng thái khác không giải quyếtđược Nếu ứng dụng không phân phối công việc đúng cách, các workerthread có thể chiếm phần lớn thời gian của chúng để ngủ Đây là nơiThread Pool trở nên hữu ích.

Thread Pool là một bể các worker thread đã được tạo sẵn và có sẵn đểứng dụng sử dụng khi cần Khi các luồng của Thread Pool hoàn thànhtask của chúng, chúng trở lại bể .NET cung cấp một thread pool đượcquản lý thông qua lớp ThreadPool mà hệ thống quản lý Chỉ có mộtthread pool duy nhất cho mỗi process.

Lớp ThreadPool có một số phương thức tĩnh, bao gồm phương thứcQueueUserWorkItem, chịu trách nhiệm gọi một luồng làm việc từ threadpool khi nó sẵn có Nếu không có luồng làm việc nào sẵn có trong threadpool, nó sẽ đợi cho đến khi có một luồng trở nên sẵn có Phương thứcQueueWorkItem nhận vào một callback hoặc delegate Sau đó, phươngthức này sẽ được thực thi trong background ngay lập tức và được tự độngtrả lại thread pool sau khi hoàn thành task

3 Thread trong C# Winform

Trong nhiều ứng dụng, có thể làm cho giao diện người dùng (UI) trở nênnhanh nhạy hơn bằng cách thực hiện các task mất thời gian trên một luồngkhác Có nhiều công cụ có sẵn để thực hiện đa luồng cho các control của

Trang 12

Windows Forms, bao gồm namespace System.Threading, phương thứcControl.BeginInvoke và BackgroundWorker component.

3.1 Tạo thread

using System;

using System.Threading;using System.Windows.Forms;namespace WinFormsThreadExample{

public partial class MainForm : Form{

public MainForm(){

private void btnStartTask_Click(objectsender, EventArgs e)

private void DoHeavyTask(){

// Giả lập công việc nặng (chẳng hạn,tính toán lâu)

for (int i = 0; i < 10; i++){

// Mô phỏng công việc nặngThread.Sleep(1000);

// cập nhật giao diện người dùngUpdateUI($"Task is in progress ({i + 1}/10)");

UpdateUI("Task completed!");}

private void UpdateUI(string message){

if (InvokeRequired){

BeginInvoke(new Action(() => UpdateUI(message)));}

txtStatus.Text = message;}

}}

Trang 13

3.2 Thread-safe

Windows Forms nói chung không có tính thread-safe, tức là không thểđảm bảo các control trong form được truy cập an toàn từ nhiều luồng khác nhaumà không làm ảnh hưởng đến chức năng của chương trình Trong trường hợpcần dùng đa luồng để tăng hiệu suất giao diện, bạn cần đảm bảo rằng mọi thayđổi trên các control đều được thực hiện từ luồng message queue.

Ta minh họa vấn đề trên bằng cách tạo form mới mang tên Form1 vớiTextBox txtResult và Button btnShow như hình dưới:

Tiếp theo, viết code cho file Form1.cs như sau:

using System;

using System.Threading;using System.Windows.Forms;namespace ThreadSafeCalls{

public partial class Form1 : Form{

private void ThreadProcUnsafe(){

private void btnShow_Click(object sender,EventArgs e)

Chạy thử chương trình và nhấn vào Button “Show”:

Trang 14

Sau 2 giây, compiler ném ngoại lệ InvalidOperationException, chothấy TextBox đã được truy cập từ một luồng khác với luồng mà nó đượckhởi tạo.

Có một cách thức để xử lý ngoại lệ nêu trên Mỗi control trongWindows Forms đều có thuộc tính InvokeRequired trả về giá trị false nếuluồng thực thi hiện tại là luồng message queue, và phương thức Invoke đểđưa một delegate có tham số vào message queue của control đó

Bằng cách này, vì delegate được gọi trực tiếp từ message queuenên chương trình sẽ không sinh ra lỗi Tuy nhiên, việc cài đặt khá phứctạp vì bạn cần phải định nghĩa một phương thức riêng cho mỗi hành độngtrên chương trình với delegate tương ứng Viết lại code cho file Form1.csnhư sau:

using System;

using System.Threading;using System.Windows.Forms;namespace ThreadSafeCalls{

delegate void SetTextCallback(string text);public partial class Form1 : Form{

private void SetText(string text){

txtResult.Text = text;}

private void ThreadProcSafe(){

Thread.Sleep(2000);

Trang 15

string text = "Written by the backgroundthread.";

if (txtResult.InvokeRequired){

SetTextCallback d =new SetTextCallback(SetText);

Invoke(d, new object[] { $"{text}(Invoke)" });

txtResult.Text = $"{text} (NoInvoke)";

public Form1(){

private void btnShow_Click(objectsender,EventArgs e)

Chạy thử chương trình, nhấn vào Button “Show” và chờ 2 giây, tađược kết quả như sau:

4 Hạn chế của multi-threading

Race Conditions và Deadlocks: Sự đồng thời của các luồng có thể dẫn

đến các vấn đề như race conditions (đối khi nhiều luồng cùng truy cập và thayđổi dữ liệu) và deadlocks (trạng thái đối với các luồng không thể tiếp tục vìchúng đang chờ lẫn nhau).

class Akshay {

Trang 16

int result = 0;

void Work1() { result = 1; } void Work2() { result = 2; } void Work3() { result = 3; } static void Main(string[] args) {

Akshay a = new Akshay();

Thread worker1 = new Thread(a.Work1); Thread worker2 = new Thread(a.Work2); Thread worker3 = new Thread(a.Work3); worker1.Start();

worker2.Start(); worker3.Start();

Console.WriteLine(a.result); Console.Read();

} }}

Nhìn vào đoạn code, có vẻ như kết quả là 3 Nhưng không, khi chạy 3 lần, sẽcho ra kết quả ngẫu nhiên hoặc 1 hoặc 2 hoặc 3 hoặc thậm chí là không.

Khó kiểm soát và Debug: Khi có nhiều luồng chạy cùng một lúc, quá

trình kiểm soát và gỡ lỗi trở nên phức tạp hơn Lỗi có thể xảy ra ở các điểmkhông đồng bộ và khó đoán trước.

Synchronization Overhead: Sự đồng bộ giữa các luồng đôi khi cần thiết

để đảm bảo tính nhất quán, nhưng việc này tạo ra overhead và có thể làm giảmhiệu suất.

Resource Overhead: Mỗi luồng đều sử dụng một phần của tài nguyên hệ

thống như bộ nhớ và các tài nguyên hạ tầng khác Số lượng lớn luồng có thể tạora áp lực đáng kể trên hệ thống.

Complexity: Việc quản lý và thiết kế các hệ thống với nhiều luồng có thể

phức tạp, đặc biệt là đối với người mới làm quen với lập trình đa luồng.

Ngày đăng: 18/06/2024, 15:38

Xem thêm: