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 trưng 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
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# 19
I. Giới thiệu về GDI+ 19
II. Kiến trỳc của GDI+ 19
II.1. Đồ họa Vector 2D 20
II.2. Hỡnh ảnh 20
II.3. In ấn và hiển thị font chữ 20
III. Kiến trỳc lớp của GDI+ 20
IV. Một số điểm mới trong GDI+ 21
IV.1. Bỳt vẽ Gradient 21
IV.2. Đường cong Spline 22
IV.3. Đối tượng đồ họa độc lập 22
IV.4. Chức năng chuyển đổi và đối tượng ma trận 22
IV.5. Vựng ảnh co giún được 23
IV.6. Đổ bỳng Alpha 23
V. Thay đổi trong mụ hỡnh lập trỡnh 23
V.1. Ngữ cảnh thiết bị, Handles và cỏc đối tượng đồ họa 23
V.2. Bỳt vẽ, bỳt phủ, đồ họa, hỡnh ảnh và Font chữ 24
VI. Giới thiệu cỏc đối tượng đồ họa cơ bản trong GDI+ 24
VI.1. Đồ họa Vector 24
VI.2. Hỡnh ảnh và Metafile 25
VI.3. Cỏc loại hệ tọa độ 26
VI.4. Cỏc phộp chuyển đổi 27
Phần III: Đa luồng trong C# 28
I. Khỏi niệm đa luồng 28
I.1. Đa nhiệm ( multitasking ) 28
I.2. Đa luồng ( multitasking) 28
II. Đa luồng trong C# 29
II.1. Cấu trỳc cỏc lớp điều khiển luồng của C# 30
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 ) 32
II.2.2. Nhập luồng ( join thread ) 33
II.2.3. Dừng một luồng 34
II.2.4. Hủy một luồng 34
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 35
II.5. Đồng bộ hỳa cỏc luồng: 37
II.5.1. Lớp Interlocked: 37
II.5.2. Sử dụng lệnh C# lock: 37
II.5.3. Monitor: 38
Phần IV: XML và C# 42
I. Lịch sử cỏc ngụn ngữ đỏnh dấu 42
I.1. Khỏi niệm “đỏnh dấu” (markup) 42
I.2. Ngụn ngữ đỏnh dấu 45
II. Tổng quan về ngụn ngữ XML 45
II.1. Ngụn ngữ XML là gỡ? 45
II.2. Cỏc ưu điểm của XML 46
II.3. Cỏc ứng dụng XML 46
II.3.1. Mathematical Markup Language (MathML) 46
II.3.2. Resource Description Framework(RDF) 47
II.3.3. XML Linking Language(XLink) 47
II.3.4. Synchronized Multimedia Intergration Language(SMIL) 47
II.3.5. Extensible Stylesheet Language(XSL) 47
II.4. Tương lai XML 47
III. Cấu trỳc và cỳ phỏp XML 48
III.1. Cấu trỳc XML 48
III.1.1. Cấu trỳc logic 48
III.1.2. Cấu trỳc vật lớ 49
III.2. Cỳ phỏp XML 50
III.2.1. Cỏc thẻ gỏn và phần tử 51
III.2.2. Cỏc thuộc tớnh và chỳ giải 52
IV. XML trong C# 52
IV.1. Tạo một tài liệu XML 52
IV.2. Duyệt tài liệu XML 55
IV.3. Quỏ trỡnh Serializing 56
IV.4. Quỏ trỡnh Deserializing 59
Phần V: Lập trỡnh mạng trong C# 60
I. Giới thiệu về lập trỡnh mạng 60
I.1. Nhận dạng mỏy 60
I.2. Socket 61
I.3. Server phục vụ nhiều clients 64
II. Giao tiếp với Web 66
II.1. Gửi và nhận cỏc yờu cầu HTTP 66
II.2. Cỏc yờu cầu Web khụng đồng bộ 68
II.3. Dịch vụ Web 68
Phần VI: Một vài so sỏnh C# với cỏc ngụn ngữ khỏc 70
I. Sự khỏc nhau giữa C# và C/C++ 70
I.1. Về mụi trường 70
I.2. Về cỏc lệnh 70
I.3. Về tổ chức chương trỡnh 71
II. Sự khỏc biệt giữa C# và Java 71
II.1. Về kiểu dữ liệu 71
II.2. Về truy cập thành phần 72
II.3. Cỏc tham số ref và out 73
II.4. Giao diện (Interfaces) 73
II.5. Về 2 từ khoỏ khai bỏo import và using 73
III. Sự khỏc biệt giữa C# và VB 6.0 73
Kết luận 75
Tài liệu tham khảo 76
Phụ lục 77
I. Mụ tả chương trỡnh minh họa 77
II. Hướng dẫn sử dụng chương trỡnh 78
III. Hướng dẫn cài đặt chương trỡnh 79
ứ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ện
OutOfMemoryException
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 phưong 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
[C#]
public ThreadState ThreadState {get;} (**)
[C#]
public bool IsAlive {get;}) (*)
(*) - Thuộc tính này có giá trị là true khi mà luồng đã được gọi và chưa chết ( tức là phương thức stop của nó chưa được gọi và phương thức kiểm soát run của nó chưa 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 lưu ý 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).
II.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 chưa kết thúc. Phương thức này cũng sinh ra hai ngoai lệ là ThreadSateException tức luồng chưa 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.
Ví dụ:
using System;
using System.Thread;
class test
{ public static void thuyet() {
Console.WriteLine(“Trangthai:”+Second.CurrentState.ThreadSate);
/*sử dụng thuộc tính CurrentThread để trả về trạng thái của luồng của luồng
hiện thời */
}
public static void Main()
{
Console.WriteLine(“Luong chinh”);
ThreadStart my=new ThreadStart(thuyet);
Thread second=new Thread(my);
second.Start();
second.Join();
Console.WriteLine(“Ket thuc:”+second.ThreadState);
}
}
Trong 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.
Ketthuc:unstarted
Trangthai:Running.
Tuy nhiên khi ta cho dòng lệnh vào thì khi đó luồng chương trình chính sẽ đi vào trạng thái Thread.WaitSleepJoin và chờ cho luồng vừa tạo ra kết thúc mới tiếp tục làm tiếp do đó đầu ra của chương trình trên:
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 ngưng 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
Trong 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.
Running
Thread.Start
WaitSleepJoin
SuspendRequested
Suspended
AbortRequested
Aborted
Thread.Abort
Thread.Resume
Thread.Sleep
Thread.Join
Monitor.Wait
Thread.Suspend
Thread.Abort
Thread.Abort
Thread.Interrupt
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 chưa 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 cho đế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 :
Lớp Interlock.
Lệnh lock C#.
Lớp Monitor.
II.5.1. Lớp Interlocked:
Lớp này cung cấp một cơ chế hoạt động tối giản trên các biến được sử dụng bởi nhiều luồng. Các phương thức của lớp này cho ta một cơ chế bảo vệ chống lại các lỗi có thể xảy ra trong khi một luồng đang cập nhật một biến mà các luồng khác lại truy nhập nhập vào.
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
Trong đó:
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í dụ trên.
try{
while(counter<1000){
lock(this){
int temp=counter;
temp++;
Thread.Sleep(1);
counter=temp;
}
Console.WriteLine("Thread {0}. Incrementer:{1}",
Thread.CurrentThread.Name,counter);
}
}
Kết quả đầu ra cũng tương tự như ví dụ trên.
II.5.3. Monitor:
Trong các điều khiển phức tạp truy nhập đến tài nguyên chúng ta sử dụng bộ giám sát Monitor. Lớp Monitor điều khiển truy nhập đến đối tượng bằng cách đảm bảo một lock trên đối tựợng cho một luồng duy nhất. Đối tượng lock đảm bảo sự giới hạn trong việc truy xuất đến một đoạn mã nào đó, thường được gọi là đoạn găng. Trong khi một luồng sở hữu một lock đối tượng, các luồng khác không thể chiếm lấy lock trừ khi luồng thực hiện xong và trả về lock cho các luồng trong hàng.
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 lock 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:
namespace Programming_CSharp
{
using System;
using System.Threading;
class Tester
{
static void Main( )
{
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
}
public void DoTest( )
{
//tạo một mảng các luồng
Thread[] myThreads =
{
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)
{
myThread.IsBackground=true;
myThread.Start( );
myThread.Name = "Thread" + ctr.ToString( );
ctr++;
Console.WriteLine("Started thread {0}", myThread.Name);
Thread.Sleep(50);
}
//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
{
Monitor.Enter(this);//đồng bộ hóa các truy nhập
if (counter < 10)
{
Console.WriteLine("[{0}] In Decrementer. Counter: {1}. Gotta Wait!",
Thread.CurrentThread.Name, counter);
Monitor.Wait(this);//nếu counter nhỏ hơn muời thì cho luồng này chờ
}
while (counter >0)
{
long temp = counter;
temp--;
Thread.Sleep(1);
counter = temp;
Console.WriteLine("[{0}] In Decrementer. Counter: {1}. ",
Thread.CurrentThread.Name, counter);
}
}
finally
{
Monitor.Exit(this);
}
}
void Incrementer( )
{
try
{
Monitor.Enter(this);
while (counter < 10)
{
long temp = counter;
temp++;
Thread.Sleep(1);
counter = temp;
Console.WriteLine("[{0}] In Incrementer. Counter: {1}",
Thread.CurrentThread.Name, counter);
}
Monitor.Pulse(this);//thông báo cho luồng khác đến lấy khóa mà dùng
}
finally
{
Console.WriteLine("[{0}] Exiting...",Thread.CurrentThread.Name);
Monitor.Exit(this);
}
}
private long counter = 0;
}
}
Đầu ra của chương trình trên như sau:
Started thread Thread1
[Thread1] In Decrementer. Counter: 0. Gotta Wait!
Started thread Thread2
[Thread2] In Incrementer. Counter: 1
[Thread2] In Incrementer. Counter: 2
[Thread2] In Incrementer. Counter: 3
[Thread2] In Incrementer. Counter: 4
[Thread2] In Incrementer. Counter: 5
Phần IV: XML và C#
I. Lịch sử các ngôn ngữ đánh dấu
I.1. Khái niệm “đánh dấu” (markup)
Như chúng ta đã biết, khái niệm “đánh dấu” ở đây ám chỉ việc gán thẻ (tagging) cho các tài liệu điện tử vì hai mục đích: định dạng văn bản hoặc tạo nên các cấu trúc theo một ý nghĩa nào đó cho một tài liệu để có thể kết xuất hoặc trao đổi, ví dụ như kết xuất ra máy in hoặc tải lên World Wide Web.
Nếu chúng ta đã từng sử dụng một trình soạn thảo HTML ví dụ như Microsoft FrontPage hay chương trình soạn thảo ví dụ như MicrosoftWord thì chắc hẳn chúng ta đã quen với khái niệm thay đổi định dạng văn bản trong một tài liệu. Vậy các chương trình đó thực hiện thao tác này ra sao? Thực ra các trình soạn thảo đó đã dùng các kí tự “đánh dấu” để định dạng văn bản. Ta xét một ví dụ sau đơn giản. Giả sử ta tạo một văn bản theo định dạng .rtf(rich text format) trong trình soạn thảo WordPad:
Hình 12 Minh hoạ soạn thảo trong WordPad
Ta có thể xem code của văn bản trên (ví dụ trong NodePad):
Hình 13 Mã của văn bản đọc bằng NotePad
Như ta thấy phần code của văn bản khác hơn nhiều so với phần hiển thị của nó .Như vậy trình soạn thảo (ở đây là WordPad) xử lí các thẻ đánh dấu trong code để hiển thị cho người sử dụng. Ví dụ như thẻ \rtf1 báo cho trình soạn thảo biết đây là văn bản theo định dạng .rtf, thẻ \par bắt đầu một dòng mới ... Trình soạn thảo chỉ có nhiệm vụ đọc các thẻ gán và diễn dịch ý nghĩa các thẻ sau đó hiển thị cho người sử dụng.
Tuy nhiên nếu ta tạo một văn bản như trên theo định dạng Word. Phần code của văn bản có dạng sau :
Hình 14 Mã của văn bản Word đọc bằng NotePad
Trình soạn thảo Word đã thêm một số thành phần vào các thẻ định dạng .rtf và lưu toàn bộ tài liệu dưới dạng file nhị phân và ta không thể đọc được dưới dạng file văn bản. Ta còn gọi đây là định dạng theo kiểu đóng tức là các hãng khác nhau có thể đưa ra các định dạng khác nhau cho file tập tin nhị phân và các trình soạn thảo riêng biệt phải hiểu rõ ý nghĩa của luồng các bit vào từ đó phiên dịch ra kết quả cho người sử dụng. Còn định dạng theo kiểu mở tức là các file nhị phân đó đã được chuẩn hoá .Ví dụ như khi trình soạn thảo đọc được các bit 1100001 thì nó sẽ diễn giải thành số ‘97’ và tập tin nhị phân được chuẩn hoá còn được gọi là tập tin văn bản hay là mang tính chất mở.
Bên cạnh việc dùng để định dạng văn bản, khái niệm “đánh dấu” còn được sử dụng để tạo ra các cấu trúc hay ngữ cảnh của các thành phần.Ví dụ như ta có thể xác định một tài liệu chỉ chứa các thành phần như tên, tuổi, ngày sinh. Ngoài ra ta còn có thể định dạng tài liệu nếu không chứa thành phần tên thì không chứa các thành phần như tuổi, ngày sinh. Mặt khác kiểu của các thành phần cũng được xác định như thành phần tên ở dưới dạng văn bản, thành phần ngày sinh dưới dạng ngày tháng và thành phần tuổi dưới dạng số. Như vậy ở đây khái niệm “đánh dấu” được dùng để thiết lập cấu trúc của tài liệu và ngữ nghĩa của các thành phần. Ta tạo ra một tài liệu có nội dung như ví dụ trên bằng ngôn ngữ “đánh dấu” HTML .Dưới đây là phần code:
Hình 15 Tạo văn bản HTML trong NotePad
Theo định dạng của ngôn ngữ HTML thì tài liệu đã được cấu trúc hoá mặc dù chưa được chặt chẽ. HTML sử dụng các thẻ để “đánh dấu”, ví dụ như nội dung của một trang HTML nằm trong cặp thẻ và . Sau đó là đến các thành phần như Head, Title, Body... Tất cả các đoạn code đều có chỗ xác định và mang ý nghĩa trong trang HTML ví dụ như thẻ “đánh dấu” và cho biết phần nội dung nằm giữa hai thẻ này phải được in đậm, thẻ xác định một chỗ xuống dòng...
I.2. Ngôn ngữ đánh dấu
Hiện nay có hai loại ngôn ngữ đánh dấu đang được sử dụng đó là ngôn ngữ đánh dấu chuyên dụng và ngôn ngữ đánh dấu chung. Ngôn ngữ đánh dấu chuyên dụng được sử dụng để tạo ra code chuyên dụng cho một trình ứng dụng nào đó hoặc thiết bị nào đó. Các ngôn ngữ này được xây dựng phục vụ cho các mục đích chuyên biệt. Ví dụ về ngôn ngữ đánh dấu chuyên dụng như ngôn ngữ HTML để định dạng tài liệu cho mục đích Web hay ngôn ngữ RTF để định dạng văn bản. Các ngôn ngữ này có đặc điểm là: số lượng các thẻ hạn chế, các tài liệu không có tính khả chuyển do được thiết kế cho một trình ứng dụng cụ thể.
Vào những năm 70 giáo sư C.F.Goldfarb (hãng IBM) và các đồng nghiệp đưa ra một phương pháp diễn đạt văn bản không xác định riêng cho một trình ứng dụng hay một thiết bị nào. Phương pháp đó có hai điểm cơ bản, đó là: các ‘đánh dấu’ biểu thị cấu trúc của tài liệu chứ không diễn đạt cho việc định dạng kiểu văn bản và các qui tắc về ‘đánh dấu’ phải được thiết lập nghiêm ngặt sao cho code dễ hiểu đối với các chương trình và con người. Kết quả của ý tưởng trên làm tiền đề cho sự ra đời ngôn ngữ DCF GML(Document Composition Facility Generalized Markup Language) do IBM phát triển .Sau đó vào năm 1986 tổ chức ISO(International Srandard Organization) phê chuẩn ngôn ngữ SGML(Standard Generalized Markup Language)
II. Tổng quan về ngôn ngữ XML
II.1. Ngôn ngữ XML là gì?
Ngôn ngữ XML là ngôn ngữ thế hệ sau của SGML hay nói cách khác XML là tập con của SGML. Không giống như ngôn ngữ HTML ( HTML được coi là một ứng dụng của SGML bởi vì HTML được tạo ra dựa trên chuẩn SGML và nó kế thừa nhiều chi tiết từ SGML), XML là tập con của SGML và XML cũng được coi là một ‘siêu ngôn ngữ’ (ngôn ngữ có thể tạo ra các ngôn ngữ khác). XML được tối ưu hoá cho việc sử dụng trên World Wide Web và nó cung cấp cho người sử dụng thêm một số tiện ích khác mà SGML không hỗ trợ.
Nếu như HTML thiên về biểu diễn thông tin thì XML thiên về việc mô tả thông tin. XML là một chuẩn được sử dụng để cấu trúc và mô tả dữ liệu mà các ứng dụng khác nhau có thể sử dụng được. Tính năng ưu việt của XML là ở chỗ nó tách biệt giữa giao diện người dùng và dữ liệu.
Như vậy XML có thể được sử dụng như là định dạng để trao đổi dữ liệu. Rất nhiều các hệ thống, được gọi là các hệ di sản (legacy systems), có thể chứa dữ liệu theo các định dạng riêng biệt và các nhà phát triển đang cố gắng kết nối các hệ thống này sử dụng Internet. Vấn đề là làm sao trao đổi dữ liệu giữa các hệ thống không tương thích với nhau. XML sẽ là giải pháp của vấn đề. Các dữ liệu có thể được chuyển sang định dạng văn bản XML mà các ứng dụng hay các hệ thống khác nhau đều sử đụng được.
Bên cạnh đó, ta có thể sử dụng XML cho các dữ liệu Web. Lúc đó trang HTML chỉ có nhiệm vụ định dạng về mặt trình bày còn dữ liệu được chứa trong file XML. Do đó ta có thể cập nhật nội dung hay thậm chí là chuyển đổi nội dung của dữ liệu sang định dạng khác mà không phải thay đổi code HTML.
Ngoài ra XML còn được sử dụng để tạo ra các kho dữ liệu công cộng. Một ví dụ điển hình là, nếu người sử dụng đang viết một bài báo cho một tạp chí. Toà soạn báo muốn đồng thời đưa bài báo lên Web site và tạp chí. Nếu như bài báo được định dạng theo RTF thì nó có thể phải được định dạng lại để có thể tải lên Web, sau đó nó phải định dạng một lần nữa để đưa vào tạp chí xuất bản. Tuy nhiên nếu bài báo đó được viết theo chuẩn XML thì nó có thể được kết xuất ra các môi trường khác nhau một cách đồng thời bởi vì dữ liệu của bài báo và cách hiển thị tương ứng là tách biệt nhau. Hơn thế nữa các chương trình ứng dụng để hiển thị dữ liệu chỉ cần được tạo ra một lần và ta có thể có thể sử dụng nó để hiển thị một số lượng bài báo tuỳ ý.
Như vậy , XML là phương thức hữu hiệu để lưu trữ dữ liệu không những chỉ cho mục đích sử dụng trên Web mà còn được sử dụng trong các ứng dụng khác nhau.
II.2. Các ưu điểm của XML
Ngôn ngữ XML nhỏ hơn và đơn giản hơn so với SGML. Như đã nói ở trên XML được tạo ra với mục đích chủ yếu cho việc ứng dụng trên Web nên nó đã được cắt bỏ nhiều thành phần không cần thiết. Các đặc tả về SGML dài 155 trang trong khi đặc tả về XML chỉ dài 35 trang.
XML bao gồm thêm tính năng siêu liên kết được mô tả như một ngôn ngữ tách biệt có tên là XLL(Extensible Linking Language). Trong khi đó SGML không bao gồm tính năng này mà nó chỉ coi đó là một thành phần được phép thêm vào.
XML bao gồm một đặc tả về ngôn ngữ định kiểu có tên là :XSL(Extensible Stylesheet Language). Đây là ngôn ngữ cung cấp cho người sử dụng các hỗ trợ về ứng dụng bản định kiểu(st