Việc lập trình các ứng dụng trên Windows bằng Java không thuận tiện, Java có nhiều hạn chế trong việc giao tiếp với các ngôn ngữ khác nh C++, Visụal Basic Java không có sự phát triển đồn
Trang 1MụC lục
Giới thiệu đề tài 6
Phần I: Giới thiệu sơ bộ về nền NET và ngôn ngữ C# 8
I Giới thiệu sơ bộ về NET 8
I.1 Giới thiệu chung về nền NET (.NET platform) .8
I.2 Kiến trúc phân lớp nền NET 9
I.3 Những đặc trng của nền NET .9
I.3.1 Phát triển đa ngôn ngữ 9
I.3.2 Chơng trình ứng dụng độc lập với hệ điều hành và bộ vi xử lí 10
I.3.3 Quản lí bộ nhớ tự động 10
I.3.4 Hỗ trợ phiên bản 10
I.4 Những thành phần của nền NET 11
Nh chúng ta đã xem ở phần trớc, có nhiều thành phần trong nền NET Trong phần này chúng ta sẽ trình bày các thành phần nổi bật về tính chất và vai trò của chúng trong cả hệ thống 11
I.4.1 CLR 11
I.4.2 Mã quản lí và mã không quản lí ( Managed/Unmanaged Code ) 11
I.4.3 Ngôn ngữ trung gian , hệ thống kiểu thông thờng và CLS 12
I.4.4 Th viện lớp cơ sở của NET 12
I.4.5 Assembly và metadata 13
I.4.6 Chơng trình dịch Just in time 13
I.4.7 Quản lí bộ nhớ ( Garbage Collection ) 13
I.4.8 Vòng đời của mã 14
II Giới thiệu sơ bộ ngôn ngữ lập trình C# .14
II.1 Lập trình hớng đối tợng trong C# .14
II.2 Những đặc điểm của ngôn ngữ C# 15
II.2.1 Các toán tử 15
II.2.2 Các kiểu dữ liệu: 15
II.2.3 Các câu lệnh 16
II.2.4 Cấu tạo của một chơng trình C# 16
II.3 C# và những vấn đề nâng cao 18
II.3.1 C# với cơ sở dữ liệu 18
II.3.2 C# với Internet 18
Phần II: Đồ họa trong C# 20
I Giới thiệu về GDI+ 20
II Kiến trúc của GDI+ 20
II.1 Đồ họa Vector 2D 21
II.2 Hình ảnh 21
II.3 In ấn v hiển thị font chữà 21
III Kiến trúc lớp của GDI+ 21
IV Một số điểm mới trong GDI+ 22
Trang 2IV.1 Bút vẽ Gradient 22
IV.2 Đờng cong Spline 23
IV.3 Đối tợng đồ họa độc lập 23
IV.4 Chức năng chuyển đổi v đối tà ợng ma trận 23
IV.5 Vùng ảnh co giãn đợc 24
IV.6 Đổ bóng Alpha 24
V Thay đổi trong mô hình lập trình 24
V.1 Ngữ cảnh thiết bị, Handles v các đối tà ợng đồ họa 24
V.2 Bút vẽ, bút phủ, đồ họa, hình ảnh v Font chữà 25
VI Giới thiệu các đối tợng đồ họa cơ bản trong GDI+ 25
VI.1 Đồ họa Vector 25
VI.2 Hình ảnh v Metafileà 26
VI.3 Các loại hệ tọa độ 27
VI.4 Các phép chuyển đổi 28
Phần III: Đa luồng trong C# 29
I Khái niệm đa luồng 29
I.1 Đa nhiệm ( multitasking ) 29
I.2 Đa luồng ( multitasking) 29
II Đa luồng trong C# 30
II.1 Cấu trúc các lớp điều khiển luồng của C# 31
II.2 Tổng quát các phơng thức của lớp Thread 32
II.2.1 Tạo luồng ( create thread ) 33
II.2.2 Nhập luồng ( join thread ) 34
II.2.3 Dừng một luồng 35
II.2.4 Hủy một luồng 35
II.3 Vòng đời của một luồng 35
II.4 Sự u tiên của luồng và định thời gian biểu cho luồng 36
II.5 Đồng bộ hóa các luồng: 38
II.5.1 Lớp Interlocked: 38
II.5.2 Sử dụng lệnh C# lock: 38
II.5.3 Monitor: 39
Phần IV: XML và C# 43
I Lịch sử các ngôn ngữ đánh dấu 43
I.1 Khái niệm “đánh dấu” (markup) 43
I.2 Ngôn ngữ đánh dấu 46
II Tổng quan về ngôn ngữ XML 46
II.1 Ngôn ngữ XML là gì? 46
II.2 Các u điểm của XML 47
II.3 Các ứng dụng XML 47
II.3.1 Mathematical Markup Language (MathML) 47
II.3.2 Resource Description Framework(RDF) 48
II.3.3 XML Linking Language(XLink) 48
II.3.4 Synchronized Multimedia Intergration Language(SMIL) 48
II.3.5 Extensible Stylesheet Language(XSL) 48
II.4 Tơng lai XML 48
Trang 3III Cấu trúc và cú pháp XML 49
III.1 Cấu trúc XML 49
III.1.1 Cấu trúc logic 49
III.1.2 Cấu trúc vật lí 50
III.2 Cú pháp XML 51
III.2.1 Các thẻ gán và phần tử 52
III.2.2 Các thuộc tính và chú giải 53
IV XML trong C# 53
IV.1 Tạo một tài liệu XML 53
IV.2 Duyệt tài liệu XML 55
IV.3 Quá trình Serializing 57
IV.4 Quá trình Deserializing 60
Phần V: Lập trình mạng trong C# 61
I Giới thiệu về lập trình mạng 61
I.1 Nhận dạng máy 61
I.2 Socket 62
I.3 Server phục vụ nhiều clients 65
II Giao tiếp với Web 67
II.1 Gửi và nhận các yêu cầu HTTP 67
II.2 Các yêu cầu Web không đồng bộ 69
II.3 Dịch vụ Web 69
Phần VI: Một vài so sánh C# với các ngôn ngữ khác 71
I Sự khác nhau giữa C# và C/C++ 71
I.1 Về môi trờng 71
I.2 Về các lệnh 71
I.3 Về tổ chức chơng trình 72
II Sự khác biệt giữa C# và Java 72
II.1 Về kiểu dữ liệu 72
II.2 Về truy cập thành phần 73
II.3 Các tham số ref và out 73
II.4 Giao diện (Interfaces) 74
II.5 Về 2 từ khoá khai báo import và using 74
III Sự khác biệt giữa C# và VB 6.0 74
Kết luận 76
Tài liệu tham khảo 77
Phụ lục 78
I Mô tả chơng trình minh họa 78
II Hớng dẫn sử dụng chơng trình 79
III Hớng dẫn cài đặt chơng trình 79
Trang 4Danh sách các hình vẽ
Hình 1 Kiến trúc nền NET 9
Hình 2 Cấu trúc CLR 11
Hình 3 Tô màu bằng bút vẽ Gradient tuyến tính .22
Hình 4 Đờng Bézier đợc tô bởi bút phủ Gradient 23
Hình 5 Chuyển đổi đồ thị 23
Hình 6 Co giãn vùng ảnh 24
Hình 7 Các mức độ trong suốt của màu nền 24
Hình 8 Hệ trục toạ độ của GDI+ 26
Hình 9 Dịch chuyển hệ toạ độ 27
Hình 10 Vòng đời của một luồng 37
Hình 11 Vòng luân phiên thực hiện luồng 37
Hình 12 Minh hoạ soạn thảo trong WordPad 43
Hình 13 Mã của văn bản đọc bằng NotePad 44
Hình 14 Mã của văn bản Word đọc bằng NotePad 44
Hình 15 Tạo văn bản HTML trong NotePad 45
Hình 16 Cấu trúc của XML 49
Hình 17 Khai báo thành phần trong XML 50
Hình 18 Minh họa cấu trúc cây của ví dụ 52
Hình 19 Kết quả chơng trình tạo tài liệu trên IE 6.0 55
Hình 20 Kết quả ví dụ Serializing trên IE 6.0 59
Trang 5
Danh s¸ch c¸c thuËt ng÷ viÕt t¾t
IE 6.0 = Internet Explorer 6.0
CLS = Common Language Spcification
CLR = Common Language Runtime
IDE = Integrated Development Environment
API = Application Programming Interface
VB = Visual Basic
VC = Visual C
VS = Visual Studio
XML = Extensible Markup Language
MSIL = IL = Microsoft Intermediate Language
COM = Component Object Model
IDL = Interface Definition Language
DLL = Dynamic Link Library
GC = Garbage Collection
JIT = Just In Time compiler
ADO = ActiveX Data Object
MS = Microsoft
SQL = Structured Query Language
GDI = Graphic Device Independence
WMF = Window MetaFile
EMF = Enhanced MetaFile
CPU = Central Processing Unit
RTF = Rich Text Format
HTML = Hyper Text Markup Language
SGML = Standard Generalized Markup Language
Trang 6Giới thiệu đề tài
Trong nhiều năm, các lập trình viên C và C++ luôn phải đối mặt với những vấn
đề đau đầu nh: sử dụng con trỏ, quản lí bộ nhớ, truyền tham trị, tham biến, xử lý danh sách, xây dựng th viện, đa kế thừa, xây dựng giao diện thân thiện với ngời dùng Vì…vậy họ luôn mong muốn, tìm kiếm một ngôn ngữ thay thế có khả năng cũng nh tính uyển chuyển mạnh nh C và C++ hơn nữa lại đơn giản hơn Vào giữa những năm 90, thế giới lập trình có sự thay đổi lớn với sự bùng nổ Internet ( Internet Boom ) và sự ra
đời của ngôn ngữ lập trình Java Ngay từ khi ra đời, Java đã cho thấy khả năng to lớn của nó trong việc phát triển các ứng dụng trên internet Hơn nữa Java còn thnàh công với tuyên bố “write once, run anywhere” cố thể tạm dich là : viết một lần, chạy trên mọi nền Thành công đó xuất phát từ ý tởng tách rời mã khi biên dịch chơng trình và mã khi chạy chơng trình, đây là điểm khác biệt lớn so với những ngôn ngữ lập trình C hay C++ Java đa ra một khái niệm mới : máy ảo Máy ảo thực hiện các công việc nh biên dịch ra mã máy, quản lí bộ nhớ hay nói cách khác, máy ảo đóng vai trò giao…tiếp giữa ứng dụng Java và môi trờng ( hệ điều hành, hay phần cứng) làm cho ứng dụng Java độc lập với môi trờng Tuy nhiên tốc độ phát triển Java lại chậm dần, và không thể đấp ứng đợc những đòi hỏi ngày càng cao của ngời dùng Các hãng phát triển Java chậm đa ra một môi trờng tích hợp IDE phục vụ cho phát triển các dự án phần mềm Việc lập trình các ứng dụng trên Windows bằng Java không thuận tiện, Java có nhiều hạn chế trong việc giao tiếp với các ngôn ngữ khác nh C++, Visụal Basic Java không có sự phát triển đồng nhất theo xu h… ớng thuận tiện cho ngời sử dụng, phải mất nhiều năm Java mới hỗ trợ đợc điều khiển Mouse – wheel, khó sử dụng th viện API của hệ điều hành, phiên bản Visual J++ của Microsoft phát triển thì lại mang nhiều nét không giống với nguyên bản Windows XP ra đời với tuyên bố không hỗ trợ máy ảo Java, không tích hợp máy ảo Java vào trình duyệt IE 6.0 đã làm
uy tín của Java suy giảm nặng nề Cuối cùng thì ngôn ngữ mà các lập trình viên mong
đợi cũng xuất hiện, đánh dấu chấm hết cho cuộc tìm kiếm ngôn ngữ lập trình kéo dài nhiều năm của các lập trình viên Đợc bắt đầu nghiên cứu từ năm 1997, vào năm
2001, Microsoft giới thiệu một platform mới .Net, đi cùng với nó là một ngôn ngữ mới - C#
C# đợc coi nh ngôn ngữ mang tính cách mạng của Microsoft Dựa trên kinh nghiệm của các ngôn ngữ trớc đó nh C, C++ và VB, C# đợc thiết kế nhằm sử dụng
đơn giản, hoàn toàn hớng đối tợng Với sự tích hợp C# với VS Net, việc phát triển các ứng dụng Windows và Web nhanh và đơn giản Có thể truy cập vào các th viện lớp của Net, C# hỗ trợ phát triển các ứng dụng ASP.Net và dịch vụ Web Bên cạnh
đó, C# tăng cờng năng suất lập trình bằng việc xoá bỏ đi những lỗi thông thờng có trong C và C++
Java thành công nhất trên 2 lĩnh vực: lập trình các ứng dụng trên server và trong giảng dạy khoa học tính trong các trờng học C# cũng có khả năng vợt trội Java trên hai lĩnh vực đó Trong đề tài này, chúng em không thể trình bày hết mọi vấn đề
Trang 7liên quan đến ngôn ngữ C#, chúng em chỉ xin giới thiệu sơ bộ về NET và C# cùng với một số vấn đề nâng cao trong ngôn ngữ C# nh sau:
1 Giới thiệu sơ bộ về nền NET và ngôn ngữ C#
Trang 8
Phần I: Giới thiệu sơ bộ về nền NET và ngôn
ngữ C#
Nếu nh Java ra đời, nổi tiếng với tuyên bố: “ write once, run anywhere”, thì ngay từ khi chào đời, C# và NET đợc các nhà thiết kế gắn với tuyên bố: “ Every language, one platform”, có thể tạm dịch là: mọi ngôn ngữ đều chạy trên một nền Nền đó chính là NET ( NET Framework) Vậy NET là gì ? Chúng ta sẽ cùng nhau tìm hiểu về NET qua các vấn đề sau :
- Giới thiệu chung về nền NET
- Những đặc điểm của nền NET
- Những thành phần của NET
I Giới thiệu sơ bộ về NET
I.1 Giới thiệu chung về nền NET (.NET platform)
Nền NET là một khái niệm mới trong khoa học máy tính; nó vợt ra ngoài khuôn khổ của một ngôn ngữ lập trình, một bộ th viện; nó cha phải là một hệ điều hành, chúng ta có thể hiểu đơn giản nó là một nền để từ đó có thể phát triển các ứng dụng cả trên Windows lẫn trên Internet thuận tiện hơn Nền NET đợc thiết kế để phục vụ các mục đích sau:
o Cung cấp một môi trờng lập trình hớng đối tợng tuyệt đối, mã của chơng trình đợc thực thi trên một máy hay cững có thể thực thi từ một máy từ
xa thông qua Internet
o Giảm thiểu tối đa xung đột giữa các version của một phần mềm
o Đem lại một môi trờng cho phép các ngôn ngữ lập trình có thể giao tiếp với nhau, tích hợp với nhau
Chú ý: chúng ta cũng cần phải phân biệt giữa hai thuật ngữ: NET và nền .NET .NET bao gồm 3 thành phần cơ bản :
o Nền NET: một nền cho phép phát triển các ứng dụng
o Các sản phẩm NET: bao gồm tất cả các sản phẩm của Microsoft dựa
trên nền NET.
o Các dịch vụ NET: các dịch vụ đợc cung cấp bởi Microsoft phục vụ cho
việc phát triển các ứng dụng chạy trên nền NET
Nh vậy nền NET chỉ là một thành phần của NET.
Nền NET gồm hai thành phần chính: Common language runtime ( CLR ) và
th viện lớp nền NET Hai thành phần này sẽ đợc trình bày cụ thể ở những phần sau
Trang 9I.2 Kiến trúc phân lớp nền NET
Hình 1 biểu diễn kiến trúc nền NET Mỗi ngôn ngữ thuộc gia đình NET ( phiên bản đầu tiên gồm các ngôn ngữ : VC.NET, VB.NET, C#, sau đó có thêm VJ# )
Hình 1 Kiến trúc nền NET
đều đợc dịch sang ngôn ngữ trung gian Microsoft ( MSIL hay gọi ngắn là IL ) – ngôn ngữ dựa theo tiêu chuẩn của Common Language Specification ( CLS ) Có 3 loại ứng dụng cơ bản là: các ứng dụng Web, các dịch vụ Web, các ứng dụng Form trên Windows Những ứng dụng này sử dụng các đối tợng, phơng thức từ th viện lớp cơ sở
và chạy trong môi trờng CLR
I.3 Những đặc trng của nền NET
Những đặc trng chủ chốt của nền NET chủ yếu nằm trong CLR, th viện lớp cơ sơ và CLS Chúng em chỉ xin trình bày một số đặc trng chúng em cho là dễ nhận biết
và nắm bắt nhất
I.3.1 Phát triển đa ngôn ngữ
Trớc đây, vấn đề sử dụng đa ngôn ngữ ( multilanguage ) hay giao thoa ngôn ngữ lập trình ( cross – language ) đã đợc đề cập nhiều khi phát triển các ứng dụng
Đa ngôn ngữ có thể hiểu là việc sử dụng nhiều ngôn ngữ phát triển một ứng dung, mỗi ngôn ngữ viết lên một phần ứng dụng Với giải pháp này, ngời lập trình có thể sử dụng một ngôn ngữ mà mình quen thuộc kết hợp sử dụng lại những đoạn mã đợc viết trên những ngôn ngữ khác phù hợp với mục đích của một phần chơng trình nhất định
để xây dựng lên một ứng dụng hoàn chỉnh Một phơng pháp truyền thống để thực hiện giải pháp này là xây dựng nên các th viện động dll Phơng pháp này đợc áp
Trang 10dụng trong VS 6.0 Mỗi ngôn ngữ đều có thể xây dựng nên một th viện dll Một ngôn ngữ khác sẽ sử dụng file dll đó nh là một phần th viện của mình Phơng pháp th hai là
sử dụng mô hình đối tợng hớng thành phần – COM ( trong đề tài này sẽ không trình bày về COM, ở đây chúng em chỉ điểm qua) Cả hai phơng pháp trên đều sử dụng ngôn ngữ định nghĩa giao diện ( IDL ) Với nền NET, chúng ta có thể thực hiện việc phối hợp ngôn ngữ dễ dàng hơn Nền NET cho phép ngôn ngữ này có thể tích hợp với ngôn ngữ khác bằng việc sử dụng ngôn ngữ trung gian là MSIL Tất cả các ngôn ngữ khi soạn thảo có thể khác nhau, sau đó đợc dich bởi một chơng trình dịch thích hợp, chúng đều trở thành dạng ngôn ngữ trung gian, khác biệt giữa các ngôn ngữ hoàn toàn bị xoá bỏ Ngôn ngữ trung gian sẽ đợc đa vào CLR để thực thi
I.3.2 Chơng trình ứng dụng độc lập với hệ điều hành và bộ vi xử lí
Ngôn ngữ trung gian IL là ngôn ngữ độc lập với bộ vi xử lí, nó là ngôn ngữ ở cấp cao hơn ngôn ngữ máy Khi nào cần thực thi, IL sẽ đợc dịch ra ngôn ngữ máy thích hợp Bất cứ hệ điều hành nào hỗ trợ nền NET thì ứng dụng NET sẽ chạy và không gặp khó khăn gì Đối với các hệ điều hành thuộc họ Windows từ Win 98 trở nên đều hỗ trợ nền NET Tháng 6 – 2001, khi mới cho ra đời NET, Microsoft đã thông báo rằng họ đã đạt đợc thoả thuận phát triển NET trên Unix, tuy nhiên đến nay vẫn cha có kết quả chính thức Tháng 10 – 2001, Microsoft cho phép Ximian, ngời
đã phát triển giao diện GNOME thông dụng trên Linux, phát triển một chơng trình dịch C# và CLR trên Linux Phiên bản đầu tiên có tên Mono có thể tìm trên www.go-mono.net Công việc hiện đang tiến hành ở giai đoạn xây dựng th viện cơ sở trên Linux
I.3.3 Quản lí bộ nhớ tự động
Rò rỉ bộ nhớ luôn là vấn đề phức tạp trong lập trình khi ta không quản lý nổi những vùng nhớ đã đợc cấp phát Trong Visual Basic, quản lý bộ nhớ đợc thực hiện bởi kĩ thuật đếm số lần truy cập Trong C và C++, cách tốt nhất để quản lý bộ nhớ là
tự mình trả lại cho hệ điều hành những vùng nhớ không dùng nữa Trong NET, có một bộ phận là GC( Garbage Collection ) làm nhiệm vụ thu hồi lại vùng nhớ hiệu quả hơn những cách trên
I.3.4 Hỗ trợ phiên bản
Những lập trình viên đã từng lập trình với th viện động DLL chắc hẳn đều biết
đến thuật ngữ ‘DLL Hell’ DLL Hell có thể miêu tả nh sau : bạn đang sử dụng một chơng trình ứng dụng với một DLL phiên bản 1.0, sau đó bạn cài thêm một ứng dụng khác cũng sử dụng một DLL giống nh vậy với phiên bản 1.1 Khi đó ứng dụng cữ lập tức sẽ có vấn đề, có thể không chạy Khi bạn thay thế DLL đó với DLL phù hợp với ứng dụng cũ thì ứng dụng mới lại không chạy Trong NET, các thành phần của đối t-ợng luôn đợc phân tách riêng rẽ, một ứng dụng chỉ load những thành phần đã đợc xây dựng, kiểm tra chạy thử với ứng dụng đó Sau khi một ứng dụng đã cài đặt và chạy thử thành công thì nó luôn chạy .NET thực hiện vấn đề này bằng cách sử dụng thêm thành phần là assemblies Những thành phần đợc đóng gói lại trong một assembly
Trang 11Assembly có chứa thông tin về phiên bản, và CLR trong NET sẽ sử dụng thông tin này để nạp đúng những thành phần phục vụ cho ứng dụng
I.4 Những thành phần của nền NET
Nh chúng ta đã xem ở phần trớc, có nhiều thành phần trong nền NET Trong phần này chúng ta sẽ trình bày các thành phần nổi bật về tính chất và vai trò của chúng trong cả hệ thống
I.4.1 CLR
CLR có thể đợc coi nh trái tim của nền NET CLR nằm ở cấp cuối cùng trong sơ đồ phân cấp của nền NET, trực tiếp giao tiếp với hệ điều hành hay các thiết bị phần cứng Vai trò của nó là nhận mã IL, dịch chuyển sang mã máy thích hợp Từ IL trở xuống CLR giống nhau cho mọi ngôn ngữ thuộc dòng NET, điều này giải quyết
đợc vấn đề đa ngôn ngữ trong một ứng dụng
Hình 2 Cấu trúc CLR
I.4.2 Mã quản lí và mã không quản lí ( Managed/Unmanaged Code )
Những mã đợc soạn thảo, dịch nhằm mục đích đợc chạy trong môi trờng CLR thì đợc gọi là mã mã quản lí ( managed code ) Có thể hiểu đơn giản hơn, mã quản lí
là loại mã mà chơng trình thực thi mã đó đợc quản lí bởi CLR và nó đợc thừa hởng mọi dịch vụ mà CLR có Thông thờng, mã quản lí là những mã đợc tích hợp sẵn ở trong các th viện lớp hay những mã đợc dịch bởi một chơng trình dịch tuân theo
Memory Management Including Garbage Collection Execution Support
CIL Compiler
Common Type system Security
C I L
Class Loader
M A C H I N E
C O D E
Trang 12chuẩn CLS tạo ra ngôn ngữ trung gian Mã không quản lí (unmanaged code) là những mã không đợc soạn thảo, dịch trong môi trờng NET và không nhằm mục đích chạy trong CLR tuy nhiên CLR vẫn nạp những mã này vào chạy, nó chỉ không hỗ trợ các dịch vụ cho loại mã này Điển hình cho loại mã này là các th viện DLL có từ trớc NET và th viện Windows APIs, những chơng trình NET sử dụng Windows APIs có nghĩa là nó đã sử dụng mã không quản lý.
I.4.3 Ngôn ngữ trung gian , hệ thống kiểu thông thờng và CLS
Ngôn ngữ trung gian MSIL trong NET, hệ thống kiểu thông thờng và CLS là
3 yếu tố gắn liền với nhau tạo nên khả năng phối hợp đa ngôn ngữ và độc lập với môi trờng của các ứng dụng NET
Hệ thống kiểu thông thờng ( common type system ) bao gồm các kiểu dữ liệu
mà các ngôn ngữ NET có thể sử dụng cũng nh qui cách ngời dùng phải tuân theo để xây dựng nên những kiểu dữ liệu của ngời dùng Các kiểu dữ liệu trong hệ thống kiểu thông thờng đợc chia thành 2 loại :
Loại tham trị: những kiểu tham trị trực tiếp lu trữ các dữ liệu, đợc cấp phát ở vùng nhớ stack Những dữ liệu kiểu này thờng là kiểu dữ liệu xây dựng sẵn nh Int, long, boolean, hay kiểu struct do ngời dùng định nghĩa
Loại tham biến: kiểu tham biến la giữ địa chỉ chỉ tới một vùng dữ liệu, chúng đợc cấp phát ở vùng nhớ Heap Những dữ liệu kiểu này thờng là các biến đối tợng
CLS ( common language specification) là một tập hợp các đặc điểm ngôn ngữ
mà tất cả các ngôn ngữ lập trình trên NET phải tuân theo, nó cũng bao gồm các kiểu dữ liệu và các qui cách trong hệ thống kiểu thông thờng Những ngời muốn phát triển một ngôn ngữ trên NET thì cũng phải dựa theo CLS để xây dựng chơng trình dịch gọi là chơng trình dịch CLS
Ngôn ngữ trung gian IL đợc dịch ra từ mã nguồn của một ngôn ngữ lập trình cấp cao bằng một chơng trình dịch CLS, ngôn ngữ trung gian IL sau đó đợc CLR dịch lại một lần nữa ra mã máy để thực thi
I.4.4 Th viện lớp cơ sở của NET
.NET có một th viện đố sộ những kiểu dữ liệu có thể sử dụng lại, đợc tích hợp chặt chẽ với CLR Th viện lớp này hoàn toàn hớng đối tợng, cung cấp những kiểu dữ liệu mà chúng ta có thể sử dụng rất nhiều chức năng từ đó Nhờ sử dụng th viện lớp cơ sở chúng ta có thể phát triển các kiểu ứng dụng sau:
ứng dụng vào ra Console
Những ứng dụng Windows với giao diện đồ hoạ
Trang 13của NET đều sử dụng th viện này, điều này làm cho việc sử dụng đa ngôn ngữ cũng
dễ dàng hơn
I.4.5 Assembly và metadata
Nếu chúng ta muốn trình bày kĩ về assembly và metadata thì cần phải có một
đề tài chuyên về mảng này, trong giới hạn đề tài này, chúng em chỉ xin trình bày mang tính khái niệm về hai vấn đề trên Assembly có thể hiểu nh là một gói cả mã chơng trình, các thành phần, các tài nguyên Một assembly bao gồm thông tin metadata, mã chơng trình ở dạng IL, các file tài nguyên ví dụ nh các file ảnh, âm nhạc, các th viện thành phần
Metadata là tập hợp dữ liệu ở dạng nhị phân diễn tả các thành phần của chơng trình Metadata đợc lu trữ ở file có thể thực thi ( executable hay exe , dll) cùng với mã IL của chơng trình Metadata chứa những loại dữ liệu cụ thể sau:
Tên assembly
Số hiệu phiên bản
Culture : thông tin về loại ngôn ngữ mà assembly hỗ trợ
Thông tin về strong name
Danh sách tất cả các file đợc đóng gói
Thông tin về tham chiếu kiểu dữ liệu: CLR sử dụng thông tin này để tìm
ra những file định nghĩ kiểu dữ liệu đó
Thông tin phục vụ cho tham chiếu đến các assembly khác
CLR hoàn toàn dựa những thông tin này để điều khiển ứng dụng Assembly và metadata đợc tạo ra ngay khi ta biên dịch mã nguồn
I.4.6 Chơng trình dịch Just in time
Chơng trình dịch Just In Time là nằm trong CLR, có nhiệm vụ chuyển mã IL sang mã máy thích hợp Trong NET có 3 loại chơng trình dịch JIT:
Pre-JIT: loại JIT này dịch ngay toàn bộ mã IL sang mã máy khi nó đợc gọi tới
Econo-JIT: loại này sử dụng cho các hệ thống hạn chế bộ nhớ, nó dịch mã IL sang mã máy từng bit một, những mã máy sau khi đợc dịch và đa vào thực thi nó còn đợc để ở vùng nhớ đệm, nếu hết vùng nhớ đệm JIT
sẽ xoá các mã máy này
Normal JIT: đây là loại ngầm định, dịch mã IL chỉ khi nó đợc gọi tới, mã máy sau khi dịch sẽ đợc đa vào thực thi đòng thời đợc đặt vào trong
bộ nhớ đệm
I.4.7 Quản lí bộ nhớ ( Garbage Collection )
Những ngời lập trình thờng gặp nhiều khó khăn khi giải quyết vấn đề cấp phát
bộ nhớ, rò rỉ bộ nhớ, công việc này làm giảm năng suất lập trình Để giải quyết vấn
đề này, NET đa ra hệ thống thu gom bộ nhớ GC Khi chơng trình đòi cấp phát thêm
bộ nhớ, bộ phận cấp phát bộ nhớ trong phần quản lí bộ nhớ trong CLR sẽ thc hiện, nếu không còn đủ bộ nhớ nó sẽ thông báo là không còn bộ nhớ để cấp phát GC bắt chạy, nó giả định rằng tất cả mọi thứ trong bộ nhớ đều có thể thu hồi Sau đó, nó xem
Trang 14toàn bộ bộ nhớ dành cho chơng trình ứng dụng, xây dựng nên một đồ thị diễn tả tất cả các vùng bộ nhớ đợc tham chiếu bởi chơng trình và tham chiếu lẫn nhau Sau xây dựng xong đồ thị, GC tiến hành thu gọn bộ nhớ Heap bằng cách di chuyển tất cả các vùng nhớ thật sự dùng về vị trí mới bắt đầu tại một vùng nhớ Heap còn trống Cuối cùng nó cập nhật lại các con trỏ trỏ đến các vùng bộ nhớ vừa đợc di chuyển Chúng ta
có thể thấy dờng nh GC thực hiện rất nhiều việc, tuy nhiên nó đợc thực thực hiện tự
động bằng CLR, giảm nhẹ đi rất nhiều công việc của ngời lập trình
I.4.8 Vòng đời của mã
Trong phần này, chúng em sẽ giới thiệu về trình làm việc của một ứng dụng NET từ khi soạn thảo mã nguồn đến khi chạy chơng trình :
Bắt đầu từ việc soạn thảo mã nguồn trên một ngôn ngữ NET quen thuộc trên một hệ soạn thảo văn bản thông thờng
Dùng một chơng trình dịch NET dịch mã nguồn ra mã IL, đồng thời xây dựng assembly cho ứng dụng
Khi chơng trình ứng dụng thực thi, hệ điều hành sẽ đọc header của
ch-ơng trình và đa CLR vào quản lí chch-ơng trình, CLR đọc các thông tin metadata, điều khiển Loader nạp các th viện cần thiết vào bộ nhớ
Hàm _CorExeMain đợc chèn vào điểm nhập của chơng trình.
Bộ phận Loader nhảy vào điểm nhập chơng trình và gọi hàm
_CorExeMain thực thi.
Khi _CorExeMain thực thi, nó gọi chơng trình dịch JIT ra thực thi.
JIT dịch mã IL sang mã máy và đa vào thực thi đồng thời đợc dự trữ ở bộ nhớ đệm để khi cần không phải dịch lại
II Giới thiệu sơ bộ ngôn ngữ lập trình C#
Có thể coi ngôn ngữ lập trình C# là ngôn ngữ lập trình đơn giản vì nó chỉ có khoảng 80 từ khoá và khoảng 12 kiểu dữ liệu xây dựng sẵn ( built-in) tuy nhiên nó hỗ trợ tất cả các mô hình lập trình : lập trình cấu trúc, lập trình hớng đối tợng và lập trình hớng thành phần ( COM )
II.1 Lập trình hớng đối tợng trong C#
C# là ngôn ngữ hoàn toàn hớng đối tợng Tính chất hớng đối tợng có thể trình bày tóm tắt nh sau:
“Tất cả đều là đối tợng” Trong C#, mọi thực thể đều đợc biểu diễn là
đối tợng, đi cùng với nó là các thuộc tính, hành vi ( method ) của thực thể đó Nh vậy, một thuộc tính hay một method chắc chắn phải thuộc về một đối tợng nào đó
Một chơng trình ứng dụng bao gồm nhiều đối tợng Khi chúng muốn một đối tợng thực hiện một công việc hay đối tợng này muốn đối tợng
Trang 15kia thực hiện một công việc, chúng ta hay các đối tợng giao tiếp với nhau bằng cách gửi thông điệp Thông điệp có thể hiểu nh một lời gọi hàm gọi một method của đối tợng nào đó làm việc.
Mỗi đối tợng có một vùng nhớ riêng
Mỗi đối tợng có một kiểu dữ liệu riêng, kiểu dữ liệu đợc định nghĩa bởi một class ( lớp )
Mỗi đối tợng có một giao diện để giao tiếp với các đối tợng khác và một phần dữ liệu đợc che giấu đối với các đối tợng khác
Ngoài ra, C# cúng cho phép ngời lập trình thực hiện các hoạt động sử dụng lại dữ liệu nh kế thừa, đa hình thái và kết tập
II.2 Những đặc điểm của ngôn ngữ C#
C# đợc xây dựng từ những ngôn ngữ tiền đặc biệt là C và C++ cho nên những
đặc điểm ngôn ngữ của C# rất giống với ngôn ngữ C, C++ Trong phần này, chúng
em sẽ trình bày một vài đặc điểm của C#
Các toán tử quan hệ: <, >, <=, >=, is, as, ==, !=
Các toán tử lô- gíc: &, ^, !, &, |, <<, >>
Các toán tử điều kiện: &&, ||, ?:
Toán tử sizeof xác định kích thớc một kiểu dữ liệu
Trong C# cũng cho phép chồng toán tử và định nghĩa các toán tử mới theo các qui tắc sau:
Toán tử một toán hạng: type_of_x operation op(x)
Toán tử hai toán hạng: type_of_x,y operation op(x,y)
Trong C# không cho phép định nghĩa lại toán tử gán
II.2.2 Các kiểu dữ liệu:
C# hỗ trợ hai loại kiểu dữ liệu là kiểu tham trị và kiểu tham biến Kiểu tham trị bao gồm các kiểu đơn giản nh char, int, float Kiểu tham biến gồm các kiểu lớp, kiểu Interface, kiểu mảng hay nói cách khác tất cả các đối tợng đều là tham biễn
Kiểu tham trị khác kiểu tham biến ở chỗ: những biến tham trị la trữ trực tiếp dữ liệu của nó, trái lại biến tham biến la trữ con trỏ trỏ tới đối tợng
C# cung cấp một tập các kiểu đợc định nghĩa trớc hầu hết đã có trong C và C++ Ngoài ra , C# lại đa thêm vào kiểu boolean, string giống nh trong Pascal
C# cho phép chuyển kiểu giống nh C nà C++
Trang 16 Lệnh swich: lệnh switch thực thi một những lệnh phụ thuộc vào giá trị một biểu thức cho trớc.
Các lệnh lặp: các lệnh lặp trong C# bao gồm các lệnh lặp while, do – while, for nh trong C
Lệnh lặp foreach ( giống nh trong VB): một lệnh lặp foreach liệt kê các thành phần trong một tập hợp, thực thi một câu lệnh cho mỗi thành phần của tập hợp đó
Các lệnh throw, try, catch: các lệnh phục vụ cho quá trình quản lí lỗi trong thời gian chạy ( runtime – error ) gồm có phát ra một lỗi ( throw ), cặp lệnh try – catch đón nhận một lỗi và đa ra hành động xử
lí lỗi
II.2.4 Cấu tạo của một chơng trình C#
Nếu nh trong C, đơn vị chơng trình lớn nhất là các hàm ( modul), trong Java và C++, đơn vị chơng trình lớn nhất là các lớp ( class) thì trong C#, đơn vị chơng trình lớn nhất là không gian tên (namespace) Một chơng trình C# chứa một hay nhiều không gian tên, trong đó một không gian tên chứa dữ liệu của chơng trình, các không gian tên còn lại là các không gian tên chứa phần dữ liệu ở các chơng trình khác đợc khai báo với từ khoá using sử dụng nh là phần th viện, ví dụ :
public string GetMessage() {
return "Hello, world";
static void Main() {
HelloMessage m = new HelloMessage();
System.Console.WriteLine(m.GetMessage());
}
}
Trang 17Trong một namespace có thể lồng một hay nhiều namespace khác, ví dụ:
- public: tất cả các đối tợng khác đều có thể truy cập đến các thành phần này
- protected: chỉ những đối tợng kế thừa từ một đối tợng mới có thể truy cập vào thành phần protected của lớp cha
- Internal: những đối tợng đợc định nghĩa trong một assembly ( các file định nghĩa chúng cùng nằm trong 1 assembly) có thể truy cập những thành phần Internal của nhau Những thành phần Internal là những thành phần không khai báo thuộc tính truy cập
- Private: thành phần này chỉ đợc truy cập từ những thành phần trong cùng 1 lớp
Cấu trúc ( struct): chúng ta có thể xây dựng một loại struct nằm ngoài các lớp và nằm trong 1 namespace nên có thể coi nó là một đơn vị chơng trình ngang với class Struct cũng khá giống với class khi nó cũng có các biến dữ liệu thành phần với thuộc tính truy cập của chúng, hàm khởi tạo Tuy nhiên struct lại là dữ liệu kiểu tham trị còn class là dữ liệu kiểu tham biến, struct không cho phép kế thừa
Giao diện ( Interface ): ngôn ngữ C++ cho phép đa thừa kế, từ đó sinh ra các nhập nhằng, rắc rối khi truy cập các thành phần của các lớp cha Trong C# không cho phép chúng ta thực hiện đa thừa kế từ nhiều lớp Tuy nhiên để thực hiện đa thừa kế, C# tạo ra một đơn vị chơng trình mới
là Interface, thay vì khai báo thừa kế từ nhiều lớp, chúng ta có thể khai báo thừa kế từ một lớp và thực thi nhiều Interface Qua đó, chúng ta cũng có thể thực hiện đa kế thừa Một Interface có thể chứa các phơng thức, thuộc tính, sự kiện , ví dụ:…
interface IExample
{
string this[int index] { get; set; }
event EventHandler E;
void F(int value);
string P { get; set; }
}
public delegate void EventHandler(object sender, Event e);
Trang 18Mọi ứng dụng trên C# đều có hàm main Hàm main chính là điểm vào của
ch-ơng trình (entry point), chch-ơng trình bắt đầu từ hàm main và kết thúc ở đây Trong hàm main chúng ta có thể khởi tạo các đối tợng ( trong lập trình giao diện Windows) hay gọi các hàm khác thực thi ( trong lập trình Console ) Hàm main phải là static và
xây dựng trong một lớp, có thể trả lại giá trị Int hay không trả về giá trị
II.3 C# và những vấn đề nâng cao
II.3.1 C# với cơ sở dữ liệu
C# cho phép thao tác thuận tiện với các loại cơ sở dữ liệu SQL Server 2000, MS access, Oracle, bằng th… viện lớp ADO.NET ADO.NET là phiên bản mới nhất của chiến lợc truy cập cơ sở dữ liệu vạn năng của Microsoft Th viện lớp ADO.NET chia thành 2 loại là lớp kết nối và lớp không kết nối Lớp kết nối là trình điều khiển kết nối cơ sở dữ liệu (Data Provider) cụ thể, có nghĩa là chúng ta phải sử dụng một NET Data Provider mà chúng hỗ trợ cơ sở dữ liệu cần làm việc Trong khi đó lớp không kết nối sử dụng bất kì NET data Provider nào Khi chúng ta cần thiết phải truy cập,
xử lí dữ liệu nguồn với các thao tác select, insert, delete, update chúng ta phải sử dụng lớp kết nối, ngợc lại nếu chỉ thao tác trên dữ liệu tạm thời thì dùng lớp không kết nối
Loại lớp kết nối gồm 4 lớp sau:
Connection: Lớp kết nối cơ sở dữ liệu
DataAdapter: Lớp chứa dữ liệu
Command: lớp chứa lệnh
DataReader: Lớp đọc dữ liệu
Loại lớp không kết nối gồm các lớp sau:
Lớp DataSet: DataSet nắm giữ Tables và Relations
Lớp DataTable: DataTable nắm giữ các Rows, Column
Lớp DataView: DataView tạo nên các view cho dữ liệu
II.3.2 C# với Internet
Từ giữa những năm 90, thế giới lập trình có sự thay đổi từ lập trình ứng dụng trên các máy riêng lẻ chuyển sang lập trình các ứng dụng trên Internet Các chơng trình ứng dụng trên Web có các u điểm nh : chi phí thấp, truy cập thuận tiện từ các nơi khác nhau Đối với các site tĩnh (static site) dù là đơn giản nhất cũng cần phải…lập trình để quản lí Web Form Lớp HttpHandler cung cấp những phơng thức đơn giản, rõ ràng để quản lí các Web Form Đối với các site động (Dynamic site), ASP.NET là th viện hoàn hảo để tạo ra những trang Web có nội dung thay đổi theo thời gian đặc biệt là thơng mại điện tử Đối với mạng ngang hàng Peer-To-Peer, C#
có các công cụ mạnh để tạo nên các hệ thống mạng ngang hàng có khả năng chia sẻ các tài nguyên
Trang 20PhÇn II: §å häa trong C#
Đồ họa là một phần không thể thiếu trong các ngôn ngữ lập trình cũng như các
bộ công cụ phát triển nhất là trong thời đại các giao diện đồ họa người sử dụng phát triển mạnh như hiện nay Thực tế là hiện nay giao diện đẹp và thân thiện đóng góp một phần không nhỏ trong thành công của các phần mềm Trong phần này chúng ta
sẽ khảo sát các công cụ đồ họa GDI+ của C# và tổng quát hơn là của môi trường phát triển ứng dụng NET
I Giíi thiÖu vÒ GDI+
GDI+ là một phân hệ của hệ điều hành MS Windows XP cung cấp những tính năng dựng hình 2D cơ bản như đồ họa vector, xử lý hình ảnh Đúng như tên của nó
đã hàm ý, GDI+ là phiên bản cải tiến của GDI (thế hệ giao diện thiết bị đồ họa trước của Windows) Nó chịu trách nhiệm hiển thị và dựng hình trên màn hình, máy in GDI+ được trang bị thêm nhiều tính năng mới và tối ưu các tính năng sẵn có
GDI+ cho phép lập trình viên viết mã độc lập ứng dụng Điều này có nghĩa là lập trình viên chỉ việc viết mã để hiển thị thông tin mà không cần quan tâm đến thiết
bị hiển thị cụ thể bên dưới Người lập trình chỉ việc gọi các hàm cung cấp bởi các lớp của GDI+ và các hàm này đến lượt chúng sẽ chịu trách nhiệm giao tiếp với một driver của thiết bị cụ thể GDI cách ly ứng dụng khỏi phần cứng và như vậy cho phép lập trình viên viết mã độc lập thiết bị
Các lớp được quản lý của GDI+ là một phần của kiến trúc Microsoft NET, môi trường để xây dựng, phân phối và chạy các dịch vụ Web XML và các ứng dụng khác
II KiÕn tróc cña GDI+
Các dịch vụ của GDI+ được chia thành 3 lớp rộng sau:
Trang 21II.1 §å häa Vector 2D
Đồ họa vector liên quan đến việc vẽ các thực thể đồ họa cơ sở như đường thẳng, đường cong hay khối hình được cho bởi một tập hợp các điểm trên một hệ tọa
độ Ví dụ, một đường thẳng có thể được xác định bằng 2 điểm cuối của nó, 1 hình chữ nhật có thể được xác định bằng cách chỉ ra 1 điểm xác định đỉnh trái trên và 1 cặp số xác định chiều cao và chiều rộng của nó Một đồ thị đơn giản có thể được xác định bởi 1 mảng các điểm được nối với nhau bởi đường thẳng 1 đường Bézier spline
là một đường cong tinh vi được xác định bởi 4 điểm điều khiển
GDI+ cung cấp các lớp và cấu trúc lưu trữ thông tin về các thực thể cơ sở, các lớp lưu trữ thông tin về cách thức mà các thực thể này được dựng và các lớp chịu trách nhiệm hiển thị hình ảnh lên thiết bị Ví dụ, cấu trúc Rectangle lưu trữ thông tin
về vị trí và kích cỡ của hình chữ nhật; lớp Pen lưu trữ thông tin về màu sắc, độ rộng đường thẳng và kiểu đường thẳng; Còn lớp Graphics có các phương thức cho dựng đường thẳng, hình chữ nhật, đồ thị và các hình hình học khác Còn có thêm vài lớp Brush lưu trữ thông tin về cách thức mà các hình này được tô màu
Ta có thể ghi lại hình ảnh vector (thực chất là một chuỗi các lệnh dựng hình) trong một metafile GDI+ cung cấp lớp Metafile để ghi lại, hiển thị và lưu các metafile Các lớp MetafileHeader và MetaHeader cho phép ta lưu trữ thông tin trong phần header của metafile
II.2 H×nh ¶nh
Có một số loại hình ảnh có thể rất khó hoặc không thể được hiển thị với các kỹ thuật của đồ họa vector Ví dụ, hình ảnh trên thanh công cụ hoặc các hình icon có thể khó mô tả bởi một tập các đường thẳng và đường cong Một bức ảnh độ phân giải cao thậm chí còn khó tạo hơn với các ký thuật đồ họa vector Các hình ảnh dạng này được lưu trữ dưới dạng các ảnh bitmap (một dãy các số biểu diễn màu sắc của từng điểm trên màn hình GDI+ cung cấp lớp Bitmap để xử lý, hiển thị và lưu các ảnh bitmap
II.3 In Ên v hiÓn thÞ font ch÷ à
Phần in ấn và hiển thị font chữ chịu trách nhiệm hiển thị văn bản bằng nhiều
loại font, cỡ chữ và kiều khác nhau GDI+ cung cấp một số lượng rất ấn tượng hỗ trợ cho các nhiệm vụ phức tạp này Một trong những các đặc tính mới của GDI+ là chống răng cưa, cho phép hiển thị chữ viết mịn hơn trên các màn hình CRT phẳng và màn hình LCD
III KiÕn tróc líp cña GDI+
Trang 22Giao diện lớp được quản lý của GDI+ chứa 60 lớp, 50 kiểu liệt kê, 8 kiểu cấu trúc Lớp Graphics là phần trung tâm của các chức năng của GDI+, nó chịu trách nhiệm dựng đường thẳng, đường cong, hình ảnh và văn bản.
Rất nhiều lớp khác hoạt động chung với lớp Graphics Ví dụ, phương thức Graphics.DrawLine sử dụng một đối tượng Pen lưu trữ các thông tin về màu sắc, độ rộng, kiểu mẫu của đường thẳng cần vẽ Phương thức Graphics.FillRectangle có thể nhận 1 con trỏ đến một đối tượng LinearGradientBrush, nó chịu trách nhiệm tô màu cho một hình chữ nhật có đổi màu theo 1 bảng màu Gradient Các đối tượng Font và StringFormat tác động lên cách thức mà đối tượng Graphics sẽ hiển thị văn bản Một đối tượng Matrix lưu trữ và thực hiện các thao tác chuyển đổi tọa độ trong đối tượng Graphics được dùng để thực hiện các phép quay, tỉ lệ và lật ảnh
GDI+ cung cấp một vài cấu trúc (ví dụ như Rectangle, Point hay Size) để tổ chức dữ liệu đồ họa Một vài lớp khác cũng phục vụ chủ yếu cho mục đích lưu trữ các cấu trúc dữ liệu Ví dụ lớp BitmapData hỗ trợ lớp Bitmap, lớp PathData hỗ trợ cho lớp GraphicsPath
GDI+ định nghĩa một vài kiểu liệt kê Chúng là các tập hợp của các hằng có liên quan Ví dụ, tập liệt kê LineJoin bao gồm các thành phần Bevel, Milter và Round xác định các kiểu nối 2 đoạn thẳng
IV Mét sè ®iÓm míi trong GDI+
IV.1 Bót vÏ Gradient
GDI+ mở rộng tính năng của GDI bằng cách thêm các bút vẽ gradient để tô màu hình vẽ, đường thẳng và miền hình vẽ Ta có thể dùng một bút vẽ Gradient tuyến tính để phủ màu một hình vẽ với màu sắc thay đổi dần dần qua từng vùng trong hình vẽ này Hình vẽ sau minh họa điều này
H×nh 3 T« mµu b»ng bót vÏ Gradient tuyÕn tÝnh
Khi ta phủ một hình vẽ với một bút vẽ gradient có rất nhiều tùy chọn quy định cách thức màu sắc sẽ thay đổi từ vùng này đến vùng khác của hình Một lựa chọn là quy định màu ở trung tâm và màu ở biên, như vậy các điểm ảnh sẽ thay đổi màu sắc một cách từ từ tính từ trung tâm ra biên Hình sau minh họa của một ảnh được tạo ra
từ một cặp đường Bézier được tô bởi một bút phủ Gradient
Trang 23Hình 4 Đờng Bézier đợc tô bởi bút phủ Gradient
IV.2 Đờng cong Spline
GDI+ hỗ trợ đường cong Spline Một đường cong Spline là một chuỗi cỏc đường cong độc lập được hợp lại để tạo thành một đường cong lớn hơn Đường spline được xỏc định bởi một mảng cỏc điểm và đi qua tất cả cỏc điểm trong mảng
đú Một đường spline là 1 đường cong trơn đi qua cỏc mỗi điểm trong mảng Hỡnh sau cho thấy 2 đồ thị: 1 đường được tạo bởi 1 đường cong spline và đường cũn lại được tạo bằng cỏch nối bằng cỏc đoạn thẳng
IV.3 Đối tợng đồ họa độc lập
Trong GDI, một đồ thị thuộc về 1 ngữ cảnh thiết bị, và đồ thị bị hủy khi mà nú được vẽ ra Trong GDI+, chức năng vẽ được thực hiện bởi 1 đối tượng Graphics và
ta cú thể tạo và duy trỡ một vài đối tượng GraphicsPath riờng rẽ với đối tượng Graphics Một đối tượng GraphicsPath khụng bị hủy bởi hành động vẽ, vỡ vậy ta cú thể dựng một đối tương GraphicsPath để vẽ nhiều lần
IV.4 Chức năng chuyển đổi v đối t à ợng ma trận
GDI+ cung cấp đối tượng Matrix, một cụng cụ mạnh để thực hiện cỏc phộp biến đổi như quay, dịch một cỏch dễ dàng và mềm dẻo Một đối tượng ma trận hoạt động cựng với đối tượng sẽ chịu biến đổi Vớ dụ đối tượng GraphicsPath cú phương thức Tranform nhận vào đối số là một ma trận 1 ma trận 3x3 cú thể được dựng để thực hiờn một phộp biến đổi hay một dóy cỏc phộp biến đổi Hỡnh sau minh họa 1 đồ thị trước và sau khi thực hiện một dóy cỏc chuyển đổi ( đầu tiờn là phộp tỉ lệ, sau là phộp quay)
Hình 5 Chuyển đổi đồ thị
Trang 24IV.5 Vùng ảnh co giãn đợc
GDI+ mở rộng rất nhiều khả năng hỗ trợ vựng ảnh của nú Trong GDI, vựng được lưu trong hệ tọa độ thiết bị và phộp biến đổi duy nhất cú thể ỏp dụng với 1 vựng ảnh là phộp dịch chuyển GDI+ lưu trữ vựng ảnh trong hệ tọa độ chung và cho phộp vựng này chịu bất kỳ phộp chuyển đổi nào lưu trữ được trong 1 ma trận chuyển đổi Hỡnh sau minh họa 1 vựng ảnh trước và sau 1 chuỗi 3 phộp chuyển đổi: tỉ lệ, quay và dịch chuyển
Hình 6 Co giãn vùng ảnh
IV.6 Đổ bóng Alpha
Trong hỡnh trờn ta cú thể thấy vựng chưa được chuyển đổi (được phủ bởi màu đỏ) qua phần đó được chuyển đổi (phủ bởi cỏc đường sọc màu xanh) Điều này được thực hiện thụng qua kỹ thuật đổ búng Alpha được hỗ trợ bởi GDI+ Với kỹ thuật đổ búng Alpha ta cú thể quy định độ trong suốt của 1 màu Khi một màu phối hợp với 1 màu nền, nếu màu càng trong suốt ta nhỡn thấy màu nền càng rừ Hỡnh sau minh họa cỏc mức độ trong suốt
Hình 7 Các mức độ trong suốt của màu nền
V Thay đổi trong mô hình lập trình
V.1 Ngữ cảnh thiết bị, Handles v các đối t à ợng đồ họa
Nếu đó viết chương trỡnh dựng GDI, lập trỡnh viờn chắc hẳn đó quen với khỏi niệm ngữ cảnh thiết bị Một ngữ cảnh thiết bị là một cấu trỳc của Windows lưu trữ khả năng của một thiết bị hiển thị cụ thể và cỏc thuộc tớnh xỏc định cỏch thức mà cỏc đối tượng sẽ được vẽ lờn thiết bị đú Một ngữ cảnh thiết bị cho 1 màn hỡnh video
Trang 25cũng được liờn kết với 1 cửa sổ trong màn hỡnh hiển thị Đầu tiờn ta sẽ nhận được một handle trỏ đến 1 ngữ cảnh thiết bị (HDC) Ta sẽ chuyển handle này cho hàm GDI thực hiện cụng việc vẽ.
Với GDI+ ta khụng phải lo về việc sử dụng cỏc handle cũng như ngữ cảnh thiết bị Đơn giản là ta chỉ phải tạo 1 đối tượng Graphics và gọi cỏc phương thức của
nú với 1 phong cỏch hướng đối tượng quen thuộc Đối tượng Graphics là trung tõm của GDI+ cũng như ngữ cảnh thiết bị là trung tõm của GDI Ngữ cảnh thiết bị và đối tượng Graphics cú vai trũ tương tự nhau nhưng cú một số khỏc biệt cơ bản giữa mụ hỡnh lập trỡnh dựa trờn cỏc handle (hay con trỏ) trong GDI và mụ hỡnh lập trỡnh hướng đối tượng trong GDI+
Đối tượng Graphics, giống như ngữ cảnh thiết bị, được liờn kết với một cửa sổ
cụ thể trờn màn hỡnh và cú cỏc thuộc tớnh quy định cỏch thức vẽ cỏc đối tượng Tuy nhiờn đối tượng Graphics lại khụng bị trúi chặt với một đối tượng bỳt vẽ, bỳt tụ, đồ thị, hỡnh ảnh hay font chữ như một ngữ cảnh thiết bị Vớ dụ, trước khi ta dựng một ngữ cảnh thiết bị để vẽ một đường thẳng ta phải gọi phương thức SelectObject để liờn kết một đối tượng bỳt vẽ với 1 ngữ cảnh thiết bị Điều này thường được biết đến như việc chọn một bỳt vẽ vào ngữ cảnh thiết bị Mọi đường thẳng được vẽ trong ngữ cảnh thiết bị sẽ dựng bỳt đó chọn Với GDI+, ta chuyển đối tượng bỳt vẽ Pen như một tham số cho phương thức vẽ đường thẳng DrawLine của lớp Graphics Ta cú thể dựng cỏc bỳt vẽ khỏc nhau trong một dóy cỏc thao tỏc vẽ đường thẳng mà khụng cần phải liờn kết một đối tượng bỳt vẽ với 1 đối tượng Graphics
V.2 Bút vẽ, bút phủ, đồ họa, hình ảnh v Font chữ à
Cỏc đối tượng Bỳt vẽ, bỳt phủ, đồ họa, hỡnh ảnh và Font chữ cú thể được tạo ra
và lưu trữ riờng biệt với đối tượng Graphics Rất nhiều phương thức vẽ của lớp Graphics nhận cỏ đối tượng này làm cỏc tham số Đõy là điều trỏi ngược với GDI khi ta phải chọn cỏc đối tượng này vừo ngữ cảnh thiết bị và chuyển handle của ngữ cảnh thiết bị như một đối số đến hàm vẽ
VI Giới thiệu các đối tợng đồ họa cơ bản trong GDI+
VI.1 Đồ họa Vector
GDI+ vẽ cỏc đường thẳng, hỡnh chữ nhật và cỏc hỡnh khỏc trờn 1 hệ trục tọa
độ Ta cú thể chọn rất nhiều hệ tọa độ nhưng hệ tọa độ mặc định cú điểm gốc là gúc trỏi trờn của màn hỡnh Đơn vị đo của hệ trục tọa độ mặc định là pixel
Trang 26Hình 8 Hệ trục toạ độ của GDI+
Đồ họa vector của GDI+ hỗ trợ một số lớp cơ bản rất hữu ớch sau:
Lớp Graphics trong GDI+ cung cấp cỏc phương thức để vẽ cỏc đối tượng trờn
DrawLine, DrawRectangle, DrawEllipse, DrawPolygon, DrawArc, DrawCurve và DrawBezier DrawBezier
Mỗi phương thức trờn đều được overload để nhận một vài danh sỏch tham số Tất cả cỏc phương thức đều làm việc kết hợp với một đối tượng bỳt vẽ Pen Để vẽ bất kỳ thứ gỡ ta phải tạo ớt nhất 2 đối tượng: một đối tượng Graphics và một đối tượng Pen Đối tượng Pen lưu trữ thụng tin về thuộc tớnh như độ rộng đường và màu sắc cho hỡnh được vẽ
GDI+ cung cấp lớp Metafile để ta cú thể ghi lại và hiển thị cỏc metafile Một metafile cũn gọi là một hỡnh ảnh vector là một hỡnh ảnh được lưu lại như một chuỗi cỏc lệnh vẽ Những lệnh vẽ được lưu trong 1 đối tượng Metafile cú thể được lưu trong bộ nhớ hay lưu trong 1 file hoặc một đối tượng stream
GDI+ cú thể hiển thị cỏc metafile được lưu dưới cỏc định dạng sau:
Windows Metafile (WMF)
Enhanced Metafile (EMF)
Trang 27 EMF+
GDI+ cú thể ghi lại được metafile trong định dạng EMF và EMF+ nhưng khụng trong định dạng WMF EMF là một phần mở rộng của GDI+ cho phộp GDI+ ghi và lưu lại Chỉ cú 2 biến đổi với định dạng EMF: EMF+ đơn và EMF+ kộp EMF+ đơn chỉ chứa cỏc bản ghi GDI+ Cỏc metafile như vậy cú thể được hiển thị bởi GDI+ nhưng khụng phải GDI EMF+ kộp cú chứa cả bản ghi GDI+ và GDI Mỗi bản ghi GDI+ lưu trong 1 EMF+ metafile là một cặp với 1 bản ghi thay thế của GDI Mỗi metafile như vậy cú thể được hiển thị bởi GDI+ hay GDI
Vớ dụ sau hiển thị 1 metafile đó được lưu trước đú dưới dạng 1 file 1 Metafile được hiển thị với gúc trờn tại điểm (100,100)
public void Example_DisplayMetafile(PaintEventArgs e)
{
Graphics myGraphics = e.Graphics;
Metafile myMetafile = new Metafile("SampleMetafile.emf");
myGraphics.DrawImage(myMetafile, 100, 100);
}
VI.3 Các loại hệ tọa độ
GDI+ dựng 3 loại hệ tọa độ khụng gian: thế giới thực, trang và thiết bị Khi ta
cú một lời gọi đến hàm myGraphics.DrawLine (myPen, 0, 0, 160, 80) điểm mà ta sẽ truyền cho phương thức DrawLine – (0,0) và (160,80) là ở trong hệ tọa độ thực Trước khi GDI+
cú thể vẽ đường thẳng lờn màn hỡnh, hệ tọa độ trải qua một dóy cỏc biến đổi 1 phộp chuyển đổi sẽ chuyểntọa độ thực về tọa độ trang và một phộp chuyển đổi khỏc chuyển hệ tọa độ trang thành hệ tọa độ thiết bị
Chẳng hạn khi ta muốn làm việc với 1 hệ thống tọa độ cú điểm 0 nằm ở giữa vựng client thay vỡ là ở gúc trỏi trờn, vớ dụ bắt đầu từ pixel 100 từ cạnh trỏi của vựng client và 50 pixel từ đỉnh của vựng client Hỡnh sau minh họa hệ tọa độ như vậy và kết quả khi gọi hàm myGraphics.DrawLine(myPen, 0, 0, 160, 80):
Hình 9 Dịch chuyển hệ toạ độ
Phộp chuyển đổi thực hiện việc ỏnh xạ hệ tọa độ thực vào hệ tọa độ trang được gọi là phộp chuyển đổi hệ tọa độ thực và được lưu trong thuộc tớnh Transform của lớp Graphics Phộp chuyển đổi thực hiện việc ỏnh xạ tọa độ trang vào hệ tọa độ thiết
bị được gọi là một phộp chuyển đổi trang Lớp Graphics cung cấp cỏc thuộc tớnh PageUnit và PageScale để xử lý cỏc chuyển đổi trang Lớp này cũng cung cấp cỏc thuộc tớnh DpiX và DpiY để xỏc định độ phõn giải điểm ảnh dọc và ngang của thiết
bị hiển thị
Trang 28VI.4 Các phép chuyển đổi
Một phộp chuyển đổi tổng thể là một phộp chuyển đổi ỏp dụng lờn tất cả cỏc đối tượng được vẽ bởi 1 đối tượng Graphics Để thực hiện một phộp chuyển đổi ta tạo 1 đối tượng Graphics, thao tỏc với thuộc tớnh Transform của nú Thuộc tớnh Transform là một đối tượng ma trận, do vậy nú cú thể lưu giữ một số lượng bất kỳ dóy cỏc phộp biến đổi Affine Phộp chuyển đổi được lưu trong thuộc tớnh Transform được gọi là phộp chuyển đổi hệ tọa độ thế giới thực Lớp Graphics cung cấp một vài
phương thức để tổng hợp một phộp biến đổi phức tạp: MultiplyTransform,
RotateTransform, ScaleTransform, và TranslateTransform Sau đõy là một vớ dụ
về phộp chuyển đổi này:
Một phộp chuyển đổi cục bộ là một phộp biến đổi ỏp dụng cho một đối tượng
cụ thể Đối tượng Graphics cho phộp tạo một ảnh đồ họa và ỏp dụng cỏc phộp chuyển đổi lờn đú Vớ dụ:
Matrix myMatrix = new Matrix();
myMatrix.Rotate(45);
myGraphicsPath.Transform(myMatrix);
myGraphics.DrawRectangle(myPen, 10, 10, 100, 50);
myGraphics.DrawPath(myPen, myGraphicsPath);
Trang 29Phần III: Đa luồng trong C#
Đa luồng ( multithreading) dờng nh là một thuật ngữ mới, tuy vậy nó lại rất quen thuộc với chúng ta nhất là trong cuộc sống hằng ngày Một ví dụ điển hình của
đa luồng mà ai cũng có thể nhận ra là hoạt động của các cơ quan trong cơ thể Cơ thể chúng ta có thể thực hiện cùng một lúc vô số công việc nh hô hấp, tuần hoàn, tiêu hoá … Các hoạt động này diễn ra đồng thời Chúng ta cũng có thể tìm thấy hình ảnh của đa luồng trong ví dụ khác là hoạt động của một chiếc xe máy Một chiếc xe máy
có thể thực hiện nhiều động tác nh là rẽ, tăng tốc, nháy đèn đồng thời Trong các…phần sau, chúng ta sẽ tìm hiểu đa luồng trong thực hiện chơng trình máy tính
I Khái niệm đa luồng
Trớc khi hiểu hoàn toàn về luồng, chúng ta sẽ tìm hiểu thế nào hệ điều hành Windows hoạt động nh thế nào trong chế độ đa nhiệm:
I.1 Đa nhiệm ( multitasking )
Trớc hết ta có thể nói Windows là một hệ thống đa nhiệm Trong chế độ đa nhiệm, hệ điều hành sẽ phân bố thời gian giữa các tiến trình và quyết định tiến trình nào nên đợc chạy kế tiếp khi tiến trình hiện hành kết thúc thời gian đợc chia sẻ trên vi
xử lý Do đó hệ điều hành ngắt tiến trình đều đặn giữa các khoảng thời gian để đa tiến trình kế tiếp trong hàng đợi vào thực hiện vì vậy không có tiến trình nào có độc quyền chiếm CPU tại bất cứ thời điểm của thời gian Số lợng thời gian đa tới mỗi tiến trình phụ thuộc vào bộ vi xử lý và hệ điều hành Thời gian xử lý cho mỗi tiến trình rất nhỏ
điều này đa ra cảm tởng rằng một tập hợp các tiến trình chạy đồng thời nhng thực tế mỗi tiến trình chạy trong môt số miligiây nào đó sau đó tới tiến trình khác và sự chuyển đổi này xảy ra rất nhanh Đây là phơng cách mà các Windows 95/98/NT hay ngay cả Unix dùng để quản lý các tiến trình Tuy nhiên ở các hệ điều hành ban đầu
nh Win 3.x và DOS thì nó lại quản lý ở chế độ đơn nhiệm (monotasking) Trong chế
độ này mỗi tiến trình có thể điều khiển CPU trong bao lâu mà nó cần, với cách thực hiện này một tiến trình ngăn chặn các tiến trình khác đợc xử lí đồng thời
I.2 Đa luồng ( multitasking)
Không gian bộ nhớ, trong đó một ứng dụng đợc thực hiện đợc gọi là tiến trình (process) Trong phạm vi một tiến trình thờng có nhiều công việc cần đợc thực hiện Quá trình thực hiện một công việc đợc gọi là một luồng (tiểu trình) Các công việc trong một tiến trình có thể thực hiện tuần tự, lúc này chỉ có một luồng chạy thực hiện
Trang 30các công việc, ta gọi đây là chế độ đơn luồng Các công việc của tiến trình cũng có thể thực hiện đồng thời, lúc này có nhiều luồng thực hiện nhiều công việc.
Nh vậy ta gặp lại hình ảnh đa nhiệm trong đa luồng, chúng có thể phân biệt chúng đơn giản nh sau:
Đa nhiệm: bao gồm các ứng dụng khác nhau chạy đồng thời dới sừ
điều khiển của hệ điều hành, chúng chia sẻ CPU và bộ nhớ
Đa luồng: chỉ tồn tại trong một ứng dụng, bao gồm các công việc của một ứng dụng đợc chạy đồng thời, chúng chia sẻ tài nguyên CPU và bộ nhớ mà hệ điều hành phân cho ứng dụng đó, chúng đợc điều khiển bởi một mô đun trong ứng dụng
Trong một ứng dụng Window có các loại luồng sau:
Luồng đơn ( Single Threading ): chỉ có một luồng trong ứng dụng và nó phải làm tất cả các công việc Luồng đó có tất cả các không gian phân phối cho process Lúc này ta có thể đồng nhất luồng với ứng dụng
Luồng Apartment ( ở đây lấy hình ảnh ứng dụng là khu nhà, mỗi luồng
là mộ căn hộ): có nhiều luồng trong ứng dụng ứng dụng sẽ định khi nào
và bao lâu cho một luồng nên thực hiện Mỗi luồng có gán cho một không gian riêng trong phạm vi không gian cho ứng dụng và các luồng khác không chia sẻ các nguồn tài nguyên đó
Luồng tự do ( Free Threading ): có nhiều luồng trong một ứng dụng và những luồng này chia sẻ nguồn tài nguyên chung Các luồng khác nhau
có thể gọi cùng một phơng thức và thuộc tính tại một thời điểm Mô hình Apartment thì hiệu quả hơn mô hình single threading bởi vì công việc đ-
ợc phân chia giữa nhiều đối tợng trong khi mô hình free thì nhanh nhất
và hữu hiệu nhất Nhng chế độ free ẩn chứa nhiều rủi ro bởi vì sự chia sẻ tài nguyên giữa nhiều đối tợng, ngời lập trình cần phải chú ý nhiều đến
sự đồng bộ và tránh các xung đột xảy ra
II Đa luồng trong C#
Trong phần trên ta thấy đợc phần nào hệ điều hành quản lý các ứng dụng cùng chạy đồng thời.Từ thực tế ta cũng có những ứng dụng tính toán phức tạp cần nhiều thời gian thực hiện hay các ứng dụng cần xử lý song song (paralell), hay ứng dụng cần lấy dữ liệu từ xa đồng thời hiển thị dữ liệu trên màn hình cho ta thấy đa luồng
đóng vai trò quan trọng trong cuộc sống của chúng ta Tuy nhiên không phải hầu hết các ngôn ngữ lập trình đều cho phép lập trình viên chỉ định các hành động diễn ra
đồng thời Thay vào đó ngôn ngữ lập trình cung cấp cho chúng ta một tập cấu trúc
đơn giản để cho một chơng trình hoạt động tại một thời điểm và chỉ tiếp tục hành
động tiếp theo khi hành động thứ nhất đã kết thúc Kiểu hoạt động đồng thời mà các máy tính ngày nay thờng cài đặt là tính năng hoạt động đồng thời nguyên thủy của hệ
điều hành vốn chỉ sử dụng bởi các lập trình viên hệ thống cực kỳ kinh nghiệm Ngôn ngữ C# cùng với các ngôn ngữ khác trong bộ NET đã biến các tính năng nguyên
Trang 31thủy đồng thời trở nên sẵn sàng cho ngời lập trình xây dựng các ứng dụng cá nhân.Lập trình viên C# có thể xây dựng ứng dụng có nhiều luồng thi hành mà mỗi luồng chỉ định rõ một phần công việc trong số nhiều phần việc của một chơng trình
và có thể thi hành đồng thời với các luồng khác.Một ứng dụng cụ thể của kỹ thuật đa luồng là khả năng tự gom rác của C# trong đó C# cung cấp một luồng gom rác tự
động chạy thu hồi phần bộ nhớ đã đợc phân phối cấp phát mà chơng trình chính không dùng nữa.Tuy nhiên bên cạnh tạo ra các ra các ứng dụng đa luồng có thể cải thiện đợc hiệu suất của ứng dụng tuy nhiên việc thêm nhiều tiểu trình có thể làm tăng
độ phức tạp của ứng dụng của chúng ta cũng nh làm làm chậm việc tính toán (do vi
xử lý phải chuyển đổi liên tục từ luồng này sang thực hiện luồng tiếp theo sau đó lại quay lại thực hiện luồng đó) và làm cho việc bảo trì và gỡ rối tăng lên
II.1 Cấu trúc các lớp điều khiển luồng của C#
Trong ngôn ngữ lập trình C# không gian tên System.Threading cung cấp các lớp, các giao diện cho việc lập trình đa luồng Trong không gian tên này bao gồm lớp ThreadPool quản lý các nhóm các luồng, lớp Timer cho phép một đại diện chuyển giao đợc gọi sau một lợng thời gian chỉ định và lớp Mutex cho sự đồng bộ giữa các luồng với nhau Ngoài ra System.Threading cũng cung cấp các lớp cho việc định mức
u tiên của các luồng, chờ đợi thông báo…
Sau đây ta có một sơ đồ cấu trúc của không gian tên System.Threading:
Classes
Class Mô tả
AutoResetEvent Thông báo một or nhiều luồng đang chờ đợi
cho một sự kiện đã xảy ra và tự động khởi
động
InterLocked Cung cấp hoạt động tối thiểu cho các biến đợc
chia sẻ bởi nhiều luồngManualResetEvent Xảy ra khi thông báo một hoặc nhiều luồng có
một sự kiện vừa xảy ra
nhập các đối tợng
RegisteredWaitHandle Diễn tả một đăng ký cái mà đã đợc đăng ký
RegisterWaitForSingleObject
SynchronizationLockExcept
ion
Ngoại lệ đợc đa ra khi một phơng thức đồng
bộ hóa đợc đa ra từ một khối mã đóng không
Trang 32ThreadInterruptedException Một ngoại lệ đợc đa ra khi một luồng bị ngắt
trong khi nó ở trong trạng thái chờ đợi
ThreadPool Cung cấp một sự dùng chung của các luồng
có thể sử dụng xử lý không đồng bộ vào ra, chờ đợi ,xử lý thời gian, xử lý chuyển công việc
ThreadStateException Ngoại lệ đợc đa ra khi luồng trong một trạng
thái không hợp cho lời gọi phơng thức
Timeout Chứa đựng một hằng chỉ định cho một khoảng
thời gian nhất định
Timer Chỉ định một cơ chế cho thực hiện một phơng
thức giữa khoảng thời gian chỉ định,
Structure:
Structure Mô tả
LockCookie Định nghĩa một lock thực hiện các văn phạm
singer_writer hoặc multiple_writer
NativeOverlapped Cung cấp một cấu trúc rõ ràng cái mà hiện từ
code không quản lý và cái đó có câứ trúc giống cấu trúc Win32 overlapped với các trờng dự trữ
thêm vào tai cuối
IOCompletionCallback Nhận mã lỗi ,số các bytes ,và các giá trị trùng khi
hoạt động vào ra hoàn thành trên threadpool
TimerCallback Mô tả phơng thức cái mà điều khiển t/thái của
Timer
Enumeration:
Enumeration Mô tả
ApartmentState Chỉ định trạng apartment của một Thread
ThreadPriority Chỉ định thời gian biểu cho Thread
ThreadState Chỉ định trạng thái của Thread
II.2 Tổng quát các phơng thức của lớp Thread
Trang 33II.2.1 Tạo luồng ( create thread )
Để tạo một luồng với lớp Thread, trớc hết ta cần có một phơng thức làm việc
nh một đại diện chuyển giao luồng Trong NET dùng đại diện chuyển giao để định nghĩa một phơng thức mà luồng mới sẽ dùng Giả sử rằng chúng ta có một phơng thức MyThread, thì phơng thức này đóng vai trò đại diện, định nghĩa cho luồng Sau
đó tạo một đối tợng của lớp Thread và constructor cho một Thread sẽ là một thông số cho đại diện ThreadStart Nh vậy đại diện ThreadState định nghĩa các phơng thức mà luồng mới tạo thành sẽ thi hành
Vi dụ:
ThreadStart mt=new ThreadStart(MyThread);
//Doan tren tao ra mot dai dien chuyen giao
//Tao mot đối tợng của lớp Thread lây tham sô là đai diện chuyển giao Thread myluong=new Thread(mt);
Tuy nhiên đến bớc trên luồng vẫn cha thực hiện đợc và vẫn ở trạng thái Unstarted và khi ta gọi phơng thức Start của lớp Thread thì hệ điều hành sẽ thay đổi trạng thái của luồng tới trạng thái ThreadState.Running Khi Start đợc gọi thì thực hiên dòng đầu tiên của phơng thức ( ở vi dụ này MyThread ) tham chiếu bởi đại diện chuyển giao Delegate Khi phơng thức Start đợc gọi thì nó có thể gây ra một số ngoại
lệ mà ta cần chú ý :
ThreadStateException Luông đã bắt rồi
SecurityException Lời gọi không có cho phép thực
hiệnOutOfMemoryException Không đủ bộ nhớ thực hiện luồng
NullReferenceException Lời gọi tham chiếu một luồng mà
null
ở đây ta cũng chú ý là một khi luồng đã ngắt thì không thể có lời gọi Start lần nữa Khi phong thức Start khởi đầu luồng này, phơng thức Start sẽ trả điều khiển cho luồng gọi ngay lập tức Khi đó luồng gọi sẽ thi hành đồng thời luồng vừa phát động
Để biết đợc trạng thái của một luồng ta có thể sử dụng thuộc tính
Thread.ThreadSate hay thuộc tính Thread.isAlive
public ThreadState ThreadState {get;} (**)
public bool IsAlive {get;}) (*)
(*) - Thuộc tính này có giá trị là true khi mà luồng đã đợc gọi và cha chết ( tức là
ph-ơng thức stop của nó cha đợc gọi và phph-ơng thức kiểm soát run của nó cha hoàn tất nhiệm vụ) ngợc lại nó có giá trị false khi luồng đã kết thúc
(**)- Giá trị của thuộc tính này trả về là trạng thái hiện hành của luồn và khởi đầu với giá trị unstarted
Khi tạo một luồng ta có thể gán tên cho luồng đó để nhấn dạng tên luồng đang chạy bằng cách sử dụng thuộc tính Thread.Name để gán thuộc tên cho luồng Điều cần lu ý khi sử dụng thuộc tính này là đây là thuộc tính chỉ ghi và một luồng chỉ đợc dặt tên cho một lần duy nhất mà thôi nếu không sẽ gây ra một lỗi ngoại lệ
InvalidOperationException (thiết đặt một yêu cầu mà tên đó đã đợc đặt rồi)
Trang 34II.2.2 Nhập luồng ( join thread )
Khi ta cần dừng xử lý một luồng và chờ đợi cho luồng thứ hai kết thúc gọi là luồng thứ nhất gia nhập luồng thứ hai hay nói cách khác là gắn đầu luồng một vào
đuôi luồng thứ hai C# cung cấp cho chúng ta ba phơng thức :
public void Join() ;(*)
public bool Join(int);(**)
public bool Join(TimeSpan);(***)
(*) Khi dùng phơng thức này thì nó sẽ đóng luồng cho đến khi luồng thứ hai bị ngắt Tuy nhiên sự chờ đợi không hạn định này có thể gây ra vấn đề nghiêm trọng đó
là bế tắc hoàn toàn (deadlock) và trì hoãn vô hạn (infinitive postponement) Phơng thức này tung ra hai lỗi ngoại lệ là ThreadSateException khi mà lời gọi cố gắng gia nhập luồng trong khi đang ở trạng thai ThreadState.Unstarted và ngoại lệ thứ hai xảy
ra là ThreadInterruptException tức là luồng bị ngắt trong khi đang chờ đợi
(**) Khi dùng phơng thức này thì luồng sẽ bị đóng cho đến khi luồng thứ hai ngắt hay thời gian chỉ định hết Phơng thức này trả lại giá trị true nếu luồng hai kết thúc hoặc trả lại false khi hết thời gian mà luồng hai cha kết thúc Phơng thức này cũng sinh ra hai ngoai lệ là ThreadSateException tức luồng cha bắt đầu hoặc do thời gian là âm ArgumentOutOfRangeException
Khi thực hiện các phơng thức này thì chúng đều làm thay đổi trạng thái của luồng gọi tới ThreadState.WaitSleepJoin và ta cần chú ý là không gọi phơng thức này khi luồng đang ở trạng thái ThreadState.Unstarted
/*sử dụng thuộc tính CurrentThread để trả về trạng thái của luồng của luồng
ThreadStart my=new ThreadStart(thuyet);
Thread second=new Thread(my);
Trang 35Trong chơng trình trên khi thực hiện nếu ta bỏ đi dòng lệnh second.Join() thì chơng trình cho ta đầu ra :
Luong chinh
Trangthai:Running
Ketthuc:unstarted
II.2.3 Dừng một luồng
Để dùng một luồng trong C# ta có thể sử dụng phơng thức
Thread.Susppend [ public void suspend();]
Tuy nhiên khi ta gọi phơng thức này, hệ thống không thực hiện ngay hành
động này ngay lập tức Thay vào đó, nó ghi nhận một luồng đình chỉ vừa yêu cầu và chờ đợi cho đến khi luồng đạt đến một điểm an toàn (safe point) trớc khi thực sự đình chỉ luồng đó hoạt động Một điểm an toàn cho một luồng là một điểm an toàn cho gom rác Phơng thức này chỉ sẽ không hiệu quả khi chúng ta gọi phơng thức này khi
mà luồng đã bị dừng và để khôi phục luồng bị đình chỉ ta sử dụng phơng thức Thread.Resume() để khôi phục lại hoạt động của luồng
Lớp Thread còn đa ra cho ta một phơng thức Thread.Sleep cũng dùng cho mục
đích nh trên Phơng thức này lấy tham số là thời gian chỉ định luồng sẽ dừng trong bao lâu sau đó khôi phục thực hiện Đây không phải là sự định thời khóa biểu cho luồng thực hiện và khi thực hiện thì phơng thức này đa luồng vào trạng thái WaitSleepJoin Để gọi một luồng ra khỏi trạng thái WaitSleepJoin chúng ta có thể sử dụng phơng thức Thread.Interrupt() để làm luồng trở lại hoạt động
II.2.4 Hủy một luồng
Phơng thức Thread Abort để làm ngng hẳn một luồng đang hoạt động, có hai phơng thức cho hoạt động này: một là không đối số hai là có đối số tuy nhiên chúng
ta thờng sử dụng phơng thức không đối số là chủ yếu:
public void Abort();
public void Abort(Object);
Khi gọi phơng thức này đa ra một ngoại lệ ThreadAbortException trong luồng
để bắt đầu hủy luồng Đây là một ngoại lệ đặc biệt có thể đón bắt bàng đoạn mã của ứng dụng, tuy nhiên cuối của đoạn mã nếu ta đa ra một phơng thức ResetAbort() thì Abort() sẽ bị bỏ qua và ngăn chặn ThreadAbortException dừng luồng hiện hành
Trên đây là những phơng thức chủ yếu của lớp Thread mà chúng ta thờng hay
sử dụng trong việc thao tác và xử lý với luồng Còn những phơng thức khác quan trọng khác nữa sẽ khảo sát trong các phần tiếp theo
II.3 Vòng đời của một luồng
Trang 36Trong phần này chúng ta sẽ tìm hiểu các trạng thái và sự chuyển đổi giữa các trạng thái của luồng và hai lớp ảnh hởng đến các ứng dụng đa luồng ở đây là lớp Thread và Monitor.
Khi một luồng mới tạo thành nó ở trạng thái unstarted, luồng duy trì ở trạng thái này cho đến khi phơng thức Thread.Start() đặt luồng vào trạng thái bắt đầu chạy Started và lập tức chuyển điều khiển đến luồng vừa gọi Luồng với mức độ u tiên cao nhất sẽ bắt đầu với trạng thái Running khi hệ điều hành gán CPU cho ứng dụng
Luồng sẽ đi vào trạng thái chết khi đại diện chuyển giao của nó bị ngắt Một chơng trình có thể buộc một luồng phải dừng bằng Thread.Abort() trên một đối tợng Thread thích hợp Phơng thức này đa ra một ngoại lệ ThreadAbortException trong luồng Khi luồng ở trong trạng thái stopped thì không có tham chiếu tới đối tợng luồng do đó bộ thu gom rác tự dộng sẽ dỡ bỏ đối tợng luồng ra khỏi bộ nhớ
Có ba phơng cách để cho một luồng đang Running vào trạng thái WaitSleepJoin Một luồng có thể gọi phơng thức Monitor.Wait() để vào trạng thái WaitSleepJoin và một luồng khi ở trạng thái này nó chỉ trở lại trạng thái Running khi
ta gọi đến phơng thức Pulse hay PulseAll của lớp Monitor Phơng thức Pulse sẽ đa luồng tiếp trong hàng đợi về trạng thái Running, còn phơng thức PulseAll sẽ đa tất cả các luồng trong hàng đợi về Running Hai phơng thức còn lại Sleep hay Join cùng với tham số sẽ cũng làm tơng tự nh trên
Bất cứ một luồng nào đang ở trạng thái WaitSleepJoin bằng các phơng thức ở trên có thể trở lại trạng thái Running bằng cách sử dụng phơng thức Thread.Interrupt() để khôi phục về trạng thái Running
Khi một phơng thức Suspend đợc dùng nó sẽ đa một luồng đang hoạt động về trạng thái bị đình chỉ Một luồng chỉ sẵn sàng khi mà phơng thức resume gọi để phục hồi luồng đó
Hình 9 minh hoạ một vòng đời của kuồng
II.4 Sự u tiên của luồng và định thời gian biểu cho luồng
Mỗi luồng có mức độ u tiên trong khoảng ThreadPriority.Lowest đến
ThreadPriority.Highest Hai giá trị này có từ thuộc tính ThreadPriority(không gian
tên System.Threading ) Luồng có thể gán một trong các giá trị u tiên sau đây:
Highest :Luồng có thể định thời gian biểu trớc các u tiên khác
AboveNormal :Luồng thiết đặt sau các luông mức uu tiên Highest và trớc các luồng co mức u tiên Normal
Normal :Luồng định trớc mức u tiên BelowNormal và sau AboveNormal Đây là thiết đặt mặc định cho luồng đợc tạo ra
BelowNormal : Đợc thiết đặt trớc Lowest và sau Normal
Trang 37
Hình 10 Vòng đời của một luồng
Lowest :Đợc thiết đặt sau bất cứ luồng nào có mức u tiên khác
Hệ điều hành Windows hỗ trợ một khái niệm gọi là timeslicing cho phép nhiều luồng cùng mức u tiên cùng chia sẻ một vi xử lý Không có timeslicing, mỗi luồng
có mức u tiên ngang nhau sẽ sử dụng processor cho đến khi hoàn thành rồi các luồng khác mới có CPU để thực hiện Với timeslicing, mỗi luồng nhận đợc một khoảng thời gian ngắn nhất định của bộ vi xử lý đợc gọi là thời luợng (quantum) xuyên suốt quá trình thực hiện luồng Tại một thời lợng hoàn thành, thậm chí nếu luồng cha hoàn thành thì processor sẽ vẫn lấy luồng trong hàng đợi của các luồng đồng u tiên vào thực hiện, nếu nh có luồng chờ đợi
Công việc của bộ định luồng là giữ luồng ở mức u tiên cao nhất vào thực hiện tại tất cả các thời điểm Nếu có hơn một luồng u tiên cao nhất thì đảm bảo rằng mỗi luồng có một thời lợng hoạt động theo kiểu vòng luân phiên
Hình 11 Vòng luân phiên thực hiện luồng
Trong sơ đồ trên ta giả thiết chỉ có một processor, luồng A,B đợc cho một thời lợng làm việc trong một vòng luân phiên cho đến khi cả hai luồng đều kết thúc Điều này có nghĩa là luồng A sẽ nhận đợc một thời gian thực hiện, sau đó luông B sẽ thực hiện, sau đó luồng A lại nhận đợc một lợng thời gian làm việc điều này cứ tiếp tục
Running
Thread.Start
Suspended AbortRequested
Aborted Thread.Abort
Thread.Resume Thread.Sleep
Thread.Join
Monitor.Wait
Thread.Suspend
Thread.Abort Thread.Abort
Thread.Interrupt
Trang 38cho đến khi hai luồng kết thúc Sau đó processor sẽ chuyển xuống các luồng có mức
độ u tiên thấp hơn ở mức dới để thực hiện
Mức độ u tiên của luồng có thể điều chỉnh với thuộc tính Priority cho phép lấy giá trị trong tập các giá trị có thể của ThreadPriority mà các giá trị này ta đã đề cập ở trên:
public enum ThreadPriority
Nếu không có tham số hợp lệ cho một luồng trong tập ThreadPriority, một ngoại lệ sẽ xảy ra ArgurmentException hoặc một ngoại lệ khác là ThreadStateException nếu luồng đã kết thúc rồi Một luồng khi đợc tạo ra khi chạy sẽ
có cài đặt mặc định mức độ u tiên Normal và các luồng sẽ chạy trên cơ sở mức độ u tiên của chúng.Thuật toán định thời gian biểu cho luồng nhằm xác định trật tự của các luồng đa ra thực hiện biến đổi theo hệ điều hành
Một luồng sẽ thực hiện cho đến khi kết thúc hoặc bị dừng hay tắc nghẽn do hoạt động vào ra hay gọi phơng thức Sleep của lớp Thread hay phơng thức wait của lớp Monitor hay bị giành quyền bởi các luồng có mức u tiên cao hơn hoặc thời lợng của nó hết hiệu lực Một luồng có mức độ cao hơn có thể trở lên sẵn sàng hoặc nếu một luồng đang bị đình chỉ (suspend) đợc phục hồi (resume)
II.5 Đồng bộ hóa các luồng:
Khi chúng ta sử dụng nhiều luồng trong một ứng dụng bộc lộ nhiều rủi ro bởi vì có nhiều hơn một luồng truy nhập đến đến cùng dữ liệu tại một thời điểm ví dụ cố gắng ghi vào một biến Do đó để ngăn chặn điều này chúng ta sử dụng đến sự đồng
bộ hóa, đồng bộ đảm bảo rằng tại một thời điểm chỉ có một luồng truy nhập đến biến của chúng ta Đồng bộ hóa cung cấp cơ chế lock khóa đối tợng cho đến khi nó kết thúc làm việc thì luồng khác mới đợc sử dụng
Có ba cơ chế đồng bộ hóa cung cấp bởi CLR :
II.5.2 Sử dụng lệnh C# lock:
Mặc dù đối tợng Interlocked trên cho ta một phơng cách tốt nếu ta muốn tăng giảm dữ liệu Tuy nhiên chúng ta muốn thao tác trên dữ liệu theo nhiều mục đích khác nữa mà tránh xảy ra xung đột giữa các luồng C# cung cấp cho ta cơ chế lock
đảm chắc rằng không có luồng nào thực hiện trên đoạn găng trong khi một luồng khác đang sử dụng nó Cú pháp :
lock(expression) statement_block
Trang 39Trong đó:
expression: chỉ định một đối tợng mà bạn muốn logon expression có thể là
loại tham biến
statement_block: thể hiện đoạn code chịu ảnh hởng của lock.
Ví dụ sau đây mô phỏng cách sử dụng lock bằng cách sửa lại đoạn mã của ví
Các thông tin sau đây cần duy trì cho mỗi đối tựong đồng bộ hóa:
Một tham chiếu tới luồng nắm giữ khóa
Một tham chiếu đến hàng đợi chứa các luồng sẵn sàng cho nhận lấy khóa
Một tham chiếu tới hàng đợi chứa đựng các luồng đang chờ đợi thông báo cho sự thay đổi trạng thái của đối tợng khóa
Bảng sau dây cho ta các hành động có thể lấy bởi các luồng truy nhập đến đối tợng lock:
Hành động Mô tả
Enter,TryEnter Hành động yêu cầu một lock đến một đối tợng, hành
động này đánh dấu sự bắt đầu của một đoạn găng
Wait Giải phóng lock trên một đối tợng tạo cơ hội cho các
luồng khác chiếm lấy lock và truy nhập đến đối tợng
Pusle,PusleAll Gửi một hay nhiều tín hiệu đến một hay nhiều luồng
đang đợi Tín hiệu thông báo tới các luồng đang đợi rằng trạng thái của đối tợng lock đã thay đổi và sở hữu
Trang 40lock của nó sẵn sàng nhờng lock.
Exit Giải phóng một lock, hoạt động này đánh dấu chấm hết
cho một đoạn găng bảo vệ bằng lock
ở đây ta sử dụng phơng thức Enter hoặc Exit để đánh đấu sự bắt đầu và kết thúc của một đoạn găng Nếu đoạn găng là một dòng lệnh liên tục, lock yêu cầu bởi phơng thức Enter đảm bảo rằng chỉ một luồng chạy trên đoạn code với đối tựợng lock Trong nhiều trờng hợp chúng ta nên đặt Enter trong try và Exit trong finally
Điều này thuận tiện này đợc sử dụng đồng bộ hóa truy nhập trên phơng thức tĩnh hoặc thể hiện của một lớp
Sau đây là một ví dụ sử dụng Monitor:
Tester t = new Tester( ); //chế nên một thể hiện của lớp hiện hành
t.DoTest( ); //chạy bên ngoài static Main
new Thread( new ThreadStart(Decrementer) ),
new Thread( new ThreadStart(Incrementer) )
};
int ctr = 1;//bắt đầu với mỗi luồng
foreach (Thread myThread in myThreads)
//chờ đợi cho các luồng kết thúc mới làm tiếp
foreach (Thread myThread in myThreads)
{
myThread.Join( );
}//sau khi kết thúc in ra câu này
Console.WriteLine("All my threads are done.");
}
void Decrementer( )
{
try