MỤC LỤC
PHIẾU GIAO NHIỆM VỤ ĐỒ ÁN TỐT NGHIỆP 1
TÓM TẮT NỘI DUNG ĐỒ ÁN TỐT NGHIỆP 3
DANH MỤC HÌNH 7
DANH MỤC BẢNG 8
LỜI NÓI ĐẦU 9
PHẦN I: NỀN TẢNG CÔNG NGHỆ 10
CHƯƠNG 1: TÌM HIỂU MÔI TRƯỜNG LẬP TRÌNH TRÊN IPHONE 10
1.1 Tổng quan về lập trình trên di động 10
1.1.1 Thị trường lập trình trên di động 10
1.1.2. Những khó khăn khi lập trình cho di động 11
1.2 Tổng quan về lập trình iPhone 12
1.2.1 Giới thiệu về iPhone 12
1. Cuộc cách mạng về công nghệ trên điện thoại di động mang tên iPhone 12
2. Giới hạn của iPhone 14
1.2.2 Các nền tảng phát triển di động hứa hẹn trong năm 2010 14
1.2.3. Môi trường lập trình iPhone 16
1.2.4. iPhone SDK: 16
1. Phân tích một ứng dụng: 16
2. Cài đặt iPhone SDK: 20
3. iPhone Simulator: 22
1.2.5 Xcode 23
1.Mô hình MVC ( Model - View - Controller) 23
2. Các template thường dùng 24
3. Sự bố trí của một project trong Xcode 24
1.2.6 Interface Builder 26
1. Mở Interface Builder: 26
2. Tạo file Interface Builder 26
3. Cửa sổ tài liệu 29
1.3 Công cụ lập trình 32
1.3.1 Ngôn ngữ Objective-C 32
1. Lịch sử của ngôn ngữ Objective-C 32
2. Lớp, đối tượng và thông điệp 33
3. Định nghĩa một lớp 37
1.3.4 Lập trình mạng 43
1. Tổng quan: 43
2. Lập trình mạng với CFNetwork 44
1.3.5 MultiThreading 54
1. Về lập trình thread 54
2. Quản lý thread 61
3. Run loop 66
1.3.6. Core Audio 70
1. Core Audio là gì? 71
2. Cơ bản về Core Audio 73
CHƯƠNG 2: TỔNG QUAN VỀ HỆ THỐNG TÌM KIẾM MOBILE PORTAL 79
2.1 Hệ thống Mobile Portal 79
2.2 Máy tìm kiếm Socbay (Socbay Search Engine) 80
2.3.1. Thu thập dữ liệu cho các dịch vụ 81
2.3.2 Phân tích truy vấn ngắn dựa trên thói quen người dùng 82
2.4 Tầng giao diện di động (Mobile Interface) 82
2.4.1 Quá trình chuẩn hóa dữ liệu cho thiết bị di động: 83
2.4.2 Thiết kế module kết nối tại client và server 84
2.5 Ứng dụng Petto 84
PHẦN II: XÂY DỰNG CHƯƠNG TRÌNH 85
CHƯƠNG 3: ỨNG DỤNG TRÊN IPHONE 85
3.1 Các loại ứng dụng trên iPhone 85
3.2 Phát biểu bài toán 85
CHƯƠNG 4: XÂY DỰNG CÁC DỊCH VỤ TÌM KIẾM ĐA PHƯƠNG TIỆN DỰA TRÊN MÁY TÌM KIẾM SOCBAY 87
4.1 Xác định yêu cầu hệ thống: 87
4.2 Đặc tả user case: 88
4.2.1. Tin tức 88
4.2.2. Đọc truyện 89
4.2.3. Địa điểm 90
4.2.4. Cẩm nang tư vấn 91
4.2.5. Hình ảnh 92
4.2.6. Nhạc MP3 93
4.3 Các vấn đề kỹ thuật, thuật toán 94
4.3.1. Việc kết nối, truyền nhận dữ liệu giữa client và server: 94
4.3.2. Hiển thị 95
4.3.3. MP3 streaming 96
4.4 Thiết kế giao diện: 98
4.4.1. Giao diện chính: 98
4.4.2. Dịch vụ Tin Tức: 99
4.4.3. Dịch vụ Truyện: 99
4.4.4. Dịch vụ Địa Điểm: 100
4.4.5. Dịch vụ Cẩm Nang Tư Vấn: 100
4.4.6. Dịch vụ Hình Ảnh: 101
4.4.7. Dịch vụ Nhạc MP3: 101
4.5 Thiết kế lớp: 102
4.5.1. Tầng Cocoa Touch Application 102
1. Lớp ứng dụng: 102
2. Dịch vụ Tin Tức: 104
3. Dịch vụ nhạc MP3 106
4.5.2. Tầng Mobile Interface: 107
4.5.3. Quan hệ giữa tầng Mobile Interface và Cocoa Touch Application: 108
TỔNG KẾT ĐÁNH GIÁ 109
1. Một số kết quả đạt được 109
2 Nhược điểm 109
3 Hướng phát triển: 110
TÀI LIỆU THAM KHẢO 111
111 trang |
Chia sẻ: netpro | Lượt xem: 2053 | Lượt tải: 5
Bạn đang xem trước 20 trang tài liệu Đồ án Hệ thống Mobile Portal - Ứng dụng Petto được phát triển trên nền tảng iPhone, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
có thể sử dụng các giao thức mạng mà không cần phải sử dụng thread. CFNetwork cũng cung cấp một số đối tượng giúp sử dụng các giao thức mạng mà không cần thực thi chi tiết. Ví dụ có thể sử dụng giao thức FTP mà không cần thực thi tất cả giao diện lập trình CFFTP.
Có một số ưu điểm của việc sử dụng CFNetwork thay vì sử dụng Cocoa framework NSURL: CFNetwork tập trong nhiều hơn vào giao thức mạng, NSURL tập trung nhiều hơn vào truy nhập dữ liệu, cũng như là truyền dữ liệu qua HTTP hay FTP. CFNetwork cung cấp nhiều khả năng cấu hình hơn NSURL. Thêm nữa, NSURL yêu cầu sử dụng Objective-C.
c. CFNetwork Infrastructure
CFNetwork dựa trên 2 giao diện lập trình là một phần của Core Foundation frame work, CFSocket và CFStream.
* CFSocket API:
Socket là mức cơ bản nhất của giao tiếp mạng. Socket hoạt động giống như buồng điện thoại. Nó cho phép kết nối với socket khác (local hay thông qua mạng) và gửi dữ liệu cho các socket đó.
Socket thường hay sử dụng nhất là BSD socket. CFSocket là một trừu tượng của BSD socket. CFSocket cung cấp hầu hết các chức năng của BSD socket, nó tích hợp socket vào run loop. CFSocket không bị giởi hạn bởi loại socket dựa trên luồng mà nó có thể điều khiển bất kỳ loại socket nào.
Để tạo đối tượng CFSocket, có thể gọi hàm CFSocketCreate hay thông qua một BSD socket bằng cách sử dụng hàm CFSocketCreateWithNative. Sau đó, tạo nguồn run-loop sử dụng hàm CFSocketCreateRunLoopSource và “đẩy” nó vào run loop sử dụng hàm CFRunLoopAddSource => cho phép hàm CFSocket callback có thể chạy bất cứ khi nào đối tượng CFSocket nhận được 1 thông điệp.
* CFStream API
Đọc và ghi stream là cách thức đơn giản để trao đổi dữ liệu đa phương tiện theo cách không phụ thuộc vào thiết bị phần cứng. Các stream có thể lấy nguồn từ dữ liệu có sẵn trong bộ nhớ, trong file hay trên mạng (sử dụng socket) và khi sử dụng stream, không phải tất cả dữ liệu đều cần phải tải vào bộ nhớ một lần.
Stream là một dãy byte được truyền tuần tự qua đường truyền. Stream chỉ được truyền theo một hướng nên nếu muốn liên lạc 2 chiều thì phải tạo ra 2 stream: input stream (để đọc) và output stream (để ghi). Ngoại trừ những stream lấy nguồn từ file, không thể di chuyển đến vị trí bất kì trong stream, sau dữ liệu stream đã được truyền đi, nó không thể lấy lại được.
CFStream là một giao diện lập trình cung cấp một trừu tượng cho các stream này với 2 đối tượng CFType mới: CFReadStream và CFWriteStream. Cả 2 loại stream đều tuân theo các quy ước lập trình của Core Foundation.
CFStream được xây dựng trên CFSocket và là nền tảng cho CFHTTP và CFFTP.
Hình 1.13 Cấu trúc CFStream API
Sử dụng stream để đọc và ghi dữ liệu:
- Tạo một đối tượng stream bằng cách chỉ rõ loại (bộ nhớ, file hay socket) và thiết lập các tùy chọn của đối tượng đó.
- Mở stream để ghi hay đọc
Khi stream đang tồn tại, lấy thông tin của stream thông qua các thuộc tính của nó. Thuộc tính của stream là tất cả thông tin về stream như là nguồn, đích, đây không phải là một phần của dữ liệu được đọc ghi, mà chỉ là thông tin về bản thân stream đó thôi. Khi không cần sử dụng nữa, stream cần được đóng và giải phóng.
Các hàm CFStream để đọc hay ghi stream sẽ chặn tiến trình hiện tại cho đến khi nào ít nhất 1 byte dữ liệu được đọc hay ghi. Để tránh ghi hay đọc stream khi stream bị chặn (đóng) sử dụng phiên bản không đồng bộ của hàm và xếp lịch stream trên run loop. Hàm callback (tự viết) được gọi khi có thể đọc và ghi mà không bị chặn.
Thêm nữa, CFStream có giao thức SSL (Secure Sockets Layer) được xây dựng sẵn. Người dùng có thể thiết lập một “từ điển” chứa thông tin SSL của stream, như là mức độ bảo mật hay chữ ký cá nhân. Sau đó nó được truyền qua stream như là thuộc tính kCFStreamPropertySSLSetting khiến stream trở thành một SSL stream.
d. Các khái niệm CFNetwork API
* CFFTP API:
Liên lạc với server FTP trở nên rất đơn giản khi sử dụng CFFTP. Sử dụng CFFTP API, có thể tạo ra stream đọc FTP (để download) và stream ghi FTP (để upload). Stream đọc và ghi FTP có thể thực thi các hàm:
- Download file từ FTP server
- Upload file lên FTP server
- Download thư mục trên FTP server
- Tạo một thư mục trên FTP server
FTP stream làm việc giống như các CFNetwork stream khác. Ví dụ, tạo một stream đọc FTP bằng cách gọi hàm CFReadStreamCreateWithFTPURL. Sau đó gọi hàm CFReadStreamGetError vào bất kỳ lúc nào để kiểm tra trạng thái của stream.
Bằng cách thiết lập của FTP Stream, lập trình có thể thích ứng các stream cho một ứng dụng cụ thể. Ví dụ, nếu server mà khi kết nối stream vào yêu cầu username và password, cần phải thiết lập các thuộc tính tương ứng để stream có thể làm việc
CFFTP stream có thể được sử dụng đồng bộ hay không đồng bộ. Để mở một kết nối tới FTP server cần phải chỉ rõ khi nào stream đọc FTP được tạo ra, gọi hàm CFReadStreamOpen. Để đọc từ stream, sử dụng hàm CFReadStreamRead và cung cấp một ánh xạ tới stream đọc, CFReadStreamRef, được trả lại khi stream đọc FTP được tạo ra. Hàm CFReadStreamRead "làm đầy" buffer với output từ FTP server.
* CFHTTP API
Để gửi và nhận thông điệp HTTP, sử dụng CFHTTP API. CFHTTP là trừu tượng của giao thức HTTP.
HTTP là giao thức yêu cầu/đáp ứng giữa client và server. Client tạo một yêu cầu. Yêu cầu này sẽ được chuyển thành luồng byte.. Sau đó, yêu cầu được gửi cho server. Yêu cầu thường là yêu cầu file, như là 1 webpage. Server trả lời, gửi trả về một chuỗi. Quá trình này lặp lại nhiều lần khi cần thiết.
Để tạo một thông điệp yêu cầu của HTTP cần phải làm các việc sau:
- Xác định cách thức yêu cầu: OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE hay CONNECT
- Xác định URL: ví dụ
- Xác định HTTP version: ví dụ version 1.0 hay 1.1
- Xác định phần đầu của thông điệp bằng cách chỉ rõ tên header, như là User-Agent, và giá trị của nó, như là MyUserAgent.
- Xác định phần thân của thong điệp
Sau khi thông điệp đã được cấu trúc hóa, phân tích nó thành luồng byte, lúc này yêu cầu có dạng tương tự như sau:
GET / HTTP/1.0\r\nUser-Agent: UserAgent\r\nContent-Length: 0\r\n\r\n
Tổng hợp từ luồng byte ngược với phân tích thành luồng byte. Với tổng hợp từ luồng byte, luồng byte thô nhận được từ client hay server được khôi phục lại trạng thái ban đầu. CFNetwork cung cấp tất cả các hàm cần thiết để lấy được loại dữ liệu, HTTP version, URL, header, và thân từ thông điệp đã được phân tích thành luồng byte.
* CFHost API
CFHost API chứa các thông về host như là: tên, địa chỉ, và các thông tin có thể lấy được. Quá trình thu thập trông tin về host được gọi là sự phân tích
Sử dụng CFHost giống như là sử dụng CFStream:
- Tạo đối tượng CFHost
- Bắt đầu phân tích đối tượng CFHost
- Lấy thông tin về địa chỉ, tên hay thông tin có thể lấy được của host
- Hủy đối tượng CFHost khi đã làm việc xong với nó.
CFHost tương thích với IPv4 và IPv6. CFHost gắn kết chặt chẽ với các phần còn lại của CFNetwork. Ví dụ, hàm CFStream : CFStreamCreatePairWithSocketToCFHost sẽ tạo một đối tượng CFStream trực tiếp từ đối tượng CFHost.
* CFNetServices API
CFNetServices API được sử dụng khi cần dịch vụ mạng Bonjour để đăng ký hay phát hiện các dịch vụ Bonjour.
Để thực thi Bonjour, CFNetServices API định nghĩa 3 loại đối tượng: CFNetService, CFNetServiceBrowser và CFNetServiceMonitor. Đối tượng CFNetService biểu diễn một dịch vụ mạng đơn lẻ, như là printer or a file server. Nó bao gồm tất cả các thông tin cần thiết cho máy khác có thể resolve server đó, như là tên, loại, domain và cổng. CFNetwServiceBrowser là một đối tượng được sử dụng để phát hiện domain and dịch vụ mạng trong domain. Và đối tượng CFNetServiceMonitor được sử dụng để giám sát thay đổi của đối tượng CFNetService.
* CFNetDiagnostics API
Ứng dụng kết nối vào mạng phụ thuộc vào sự kết nối ổn định. Nếu mạng bị "rớt", nó sẽ gây ra một số vấn đề với ứng dụng. Bằng cách làm theo CFNetDiagnostics API, người sử dụng có thể tự chuẩn đoán các vấn đề liên quan tới mạng như là:
- Physical connection failure: Hỏng kết nối do lý do vật lý (cáp không được cắm vào)
- Network failure (DNS hay DHCP server không còn respond)
- Configuration failure (cấu hình proxy không đúng).
Khi network failure được chuẩn đoán, CFNetDiagnostics hướng dẫn người dùng sửa lỗi.
e. Làm việc với Stream
* Làm việc với stream đọc
Để tạo một stream đọc:
CFReadStreamRef myReadStream = CFReadStreamCreateWithFile(kCFAllocatorDefault, fileURL);
Tham số kCFAllocatorDefault chỉ ra rằng bộ cấp phát bộ nhớ mặc định hiện tại của hệ thống được sử dụng để cấp phát bộ nhớ cho stream. Tham số fileURL chỉ ra tên của file mà stream đọc sẽ đọc.
Khi stream đã được tạo ra, có thể mở stream đọc. Mở một stream khiến cho stream đó giữ được các tài nguyên hệ thống mà nó cần, ví dụ như mô tả file (cần thiết để mở một file)
Ví dụ mở một stream đọc:
if (!CFReadStreamOpen(myReadStream)) {
CFStreamError myErr = CFReadStreamGetError(myReadStream);
// An error has occurred.
if (myErr.domain == kCFStreamErrorDomainPOSIX) {
// Interpret myErr.error as a UNIX errno.
} else if (myErr.domain == kCFStreamErrorDomainMacOSStatus) {
// Interpret myErr.error as a MacOS error code.
OSStatus macError = (OSStatus)myErr.error;
// Check other error domains.
}
}
Hàm CFReadStreamOpen trả lại TRUE nếu thành công, FALSE nếu thất bại. Nếu CFReadStreamOpen trả lại FALSE, ví dụ trên sẽ gọi hàm CFReadStreamGetError để lấy về thông tin của lỗi được biểu diễn trong CFStreamError gồm 2 giá trị: mã domain và mã lỗi. Mã domain biểu thị cách thức mã lỗi được biên dịch.
Ví dụ:
kCFStreamErrorDomainPISIX -> mã lỗi là giá trị lỗi của UNIX
kCFStreamErrorDomainMacOSStatus -> mã lỗi là giá trị OSStatus được định nghĩa trong MacErrors.h
kCFStreamErrorDomainHTTP -> mã lỗi là giá trị được định nghĩa trong CFStreamErrorHTTP enumeration.
Mở một stream có thể là một quá trình tốn nhiều thời gian, nên hàm CFReadStreamOpen và CFWriteStreamOpen tránh bị chặn bằng cách trả lại TRUE biểu thị rằng quá trình mở stream đã bắt đầu. Để kiểm tra trạng thái của quá trình mở, gọi hàm CFReadStreamGetStatus và CFWriteStreamGetStatus --> trả về kCFStreamStatusOpening nếu quá trình mở vẫn đang được xử lý, kCFStreamStatusOpen nếu quá trình mở đã hoàn tất, hay kCFStreamStatusErrorOccurred nếu quá trình mở đã hoàn tất nhưng bị hỏng.
*Làm việc với stream ghi:
Làm việc với stream ghi tương tự với stream đọc. Điểm khác biệt chính là hàm CFWriteStreamWrite không trả về lượng byte được truyền vào cho nó mà trả lại số byte nó thực sự ghi.
Ví dụ tạo và sử dụng stream ghi:
CFWriteStreamRef myWriteStream =
CFWriteStreamCreateWithFile(kCFAllocatorDefault, fileURL);
if (!CFWriteStreamOpen(myWriteStream)) {
CFStreamError myErr = CFWriteStreamGetError(myWriteStream);
// An error has occurred.
if (myErr.domain == kCFStreamErrorDomainPOSIX) {
// Interpret myErr.error as a UNIX errno.
} else if (myErr.domain == kCFStreamErrorDomainMacOSStatus) {
// Interpret myErr.error as a MacOS error code.
OSStatus macError = (OSStatus)myErr.error;
// Check other error domains.
}
}
UInt8 buf[] = "Hello, world";
UInt32 bufLen = strlen(buf);
while (!done) {
CFTypeRef bytesWritten = CFWriteStreamWrite(myWriteStream, buf, strlen(buf));
if (bytesWritten < 0) {
CFStreamError error = CFWriteStreamGetError(myWriteStream);
reportError(error);
} else if (bytesWritten == 0) {
if (CFWriteStreamGetStatus(myWriteStream) == kCFStreamStatusAtEnd) {
done = TRUE;
}
} else if (bytesWritten != strlen(buf)) {
// Determine how much has been written and adjust the buffer
bufLen = bufLen - bytesWritten;
memmove(buf, buf + bytesWritten, bufLen);
// Figure out what went wrong with the write stream
CFStreamError error = CFWriteStreamGetError(myWriteStream);
reportError(error);
}
}
CFWriteStreamClose(myWriteStream);
CFRelease(myWriteStream);
myWriteStream = NULL;
* Ngăn không cho bị chặn (blocking) khi làm việc với stream
Khi làm việc với stream, đặc biệt là với stream dựa trên socket, thường xuyên xảy ra trường hợp dữ liệu truyền mất nhiều thời gian. Nếu stream được sử dụng theo cách đồng bộ thì toàn bộ ứng dụng sẽ bị buộc phải chờ cho đến khi dữ liệu được truyền xong. Trong thời gian đó, không thể tác đồng gì lên ứng dụng. Vì vậy cần phải có một phương pháp để ngăn ngừa tình trạng bị chặn.
Có hai cách để ngăn bị chặn khi đọc hoặc vào một đối tượng CFStream:
- Polling: đối với stream đọc, xác định trước có byte để đọc trước khi đọc từ stream. Đối với stream ghi, xác định trước khi nào stream có thể ghi mà không bị chặn trước khi ghi vào stream.
- Sử dụng một run-loop: Đăng ký để nhận được các sự kiện liên quan đến stream và lập lịch stream trên một run loop. Khi một sự kiện liên quan đến stream xảy ra, hàm gọi lại (tự viết) sẽ được gọi.
1.3.5 MultiThreading
1. Về lập trình thread
a. Thread là gì?
Thread là cách thức tương đối nhẹ để thực hiện nhiều tác vụ cùng lúc trong một ứng dụng. Ở mức hệ thống, các chương trình chạy cạnh nhau và được hệ thống phân phát cho thời gian thực hiện tùy thuộc vào chương trình đó cần bao nhiêu thời gian thực hiện và thời gian thực hiện của các chương trình khác. Tuy nhiên, trong một chương trình, có thể tồn tại một hay nhiều thread được sử dụng để thực thi các tác vụ khác nhau đồng thời hay gần như đồng thời. Hệ thống tự nó quản lý các thread này, lập kế hoạch cho các thread và tạm thời ngắt chúng khi cần thiết để cho phép thread khác chạy.
Xét độ về góc độ kỹ thuật thì thread là sự kết hợp giữa cấu trúc dữ liệu cấp hạt nhân và ứng dụng, cần thiết để quản lý việc thực thi code. Cấu trúc cấp hạt nhân phối hợp các sự kiện và lập lịch ưu tiên cho các thread với một trong các core có sẵn. Cấu trúc cấp ứng dụng gồm một ngăn xếp chứa lời gọi hàm và các cấu trúc ứng dụng cần để quản lý và sử dụng các thuộc tính và trạng thái của thread.
Trong một ứng dụng hoạt động không đồng thời, chỉ có một thread được thực hiện. Thread đó bắt đầu và kết thúc với main rountine và chuyển từ phương thức này sang phương thức khác để thực hiện hành vi tổng thể. Ngược lại, ứng dụng hoạt động đồng thời bắt đầu với một thread và add thêm các thread khi cần thiết. Mỗi một thread mới có start rountine của riêng nó và chạy độc lập với mã trong main rountine của chương trình chính. Ứng dụng cho phép chạy nhiều thread có 2 ưu điểm quan trọng:
- Multiple thread giúp tăng khả năng đáp ứng của ứng dụng
- Multiple thread tăng thời gian thực hiện thực sự trên hệ thống đa lõi.
Với ứng dụng chỉ có một thread, thread đó phải làm mọi thứ. Nó phải đáp ứng các sự kiện, cập nhật cửa sổ ứng dụng, và thực hiện tất cả các tính toán cần thiết. Vấn đề của việc chỉ có duy nhất một thread là nó chỉ có thể làm một việc một lúc. Vậy, điều gì xảy ra khi một trong các tính toán cần nhiều thời gian để hoàn thành? Trong khi việc tính toán vẫn được thực hiện, chương trình sẽ ngừng áp ứng các sự kiện cũng như cập nhật cửa sổ. Nếu tình trạng này xảy ra đủ lâu, người dùng có thể nghĩ rằng chương trình bị treo và cố gắng tắt nó. Tuy nhiên vấn đề này có thể dễ dàng được giải quyết nếu chuyển công việc tính toán trên sang một thread riêng biệt, khi đó main thread của chương trình vẫn được tự do để đáp ứng các tương tác của người dùng một cách kịp thời.
Với các máy tính đa lõi phổ biến hiện nay, thread giúp tăng hiệu suất của một số chương trình. Các thread có thể có thể thực hiện tác vụ khác nhau một cách đồng thời trên các lõi xử lý khác nhau, giúp cho chương trình có thể tăng số lượng công việc làm được trong một khoảng thời gian.
Tuy nhiên, thread không phải là liều thuốc chữa bách bệnh cho vấn đề hiệu suất thực thi của chương trình. Cùng với các lợi ích, thread cũng mang đến các vấn đề tiềm tàng. Có nhiều đường thực thi trong ứng dụng tương đương với việc làm tăng độ phức tạp cho code. Mỗi thread phải phối hợp hành động của nó với các thread khác để ngăn nó khỏi làm hỏng thông tin trạng thái của chương trình. Vì các thread trong một ứng dụng chia sẻ cùng một không gian bộ nhớ, chúng có quyền truy suất vào tất cả các cấu trúc dữ liệu giống nhau. Nếu có 2 thread cố gắng thao tác trên cùng một cấu trúc dữ liệu tại cùng một thời điểm, một thread có thể ghi đè lên thay đổi của thread kia gây lỗi chương trình.
b. Lựa chọn thay thế thread
Thread là cách ở mức tương đối thấp và phức tạp để hỗ trợ xử lý đồng thời trong chương trình. Nếu không hiểu đầy đủ ý nghĩa của việc mình đang làm, lập trình viên có thể dễ dàng gặp các vấn đề về đồng bộ hóa hay thời gian, mức độ nghiêm trọng trong đó có thể là từ những thay đổi nhỏ cho đến làm cho toàn bộ chương trình bị sụp đổ.
Một yếu tố khác cần xem xét là liệu có cần thread hay xử lý đồng thời. Thread giải quyết vấn đề làm thế nào để thực thi nhiều tác vụ đồng thời trong cùng một xử lý. Có những trường hợp có một số công việc không được thực hiện đồng thời. Thread cũng gây ra sự tiêu tốn về bộ nhớ và CPU time.
Bảng sau liệt kê một số lựa chọn thay thế thread. Bảng này bao gồm cả công nghệ thay thế cho thread (chẳng hạn như các đối tượng hoạt động và GCD) và lựa chọn thay thế được hướng tới hiệu quả sử dụng single thread mà chương trình đã có
Công nghệ
Mô tả
Đối tượng hoạt động (Operation objects)
Được giới thiệu trong Mac OS X v10.5, đối tượng hoạt động là wrapper cho một tác vụ thường được thực hiện trên thread thứ hai. Wrapper này giấu những khía cạnh quản lý thread để thực hiện tác vụ giúp lập trình viên tập trung vào bản thân các tác vụ. Các đối tượng này thường được sử dụng cùng với một đối tượng hàng đợi quản lý việc thực thi của các đối tượng hoạt động trên một nhiều thread.
Grand Central Dispatch (GCD)
Được giới thiệu trong Mac OS X v10.6, Grand Central Dispatch là một thay thế khác cho thread khiến người lập trình tập trung vào các nhiệm vụ cần thực hiện hơn là quản lý thread. Với GCD, cần xác định nhiệm vụ muốn thực hiện và thêm nó vào một hàng đợi. Hàng đợi này sẽ điều khiển việc lập lịch cho các tác vụ trên một thread thích hợp. Hàng đợi đưa vào tài khoản số lượng các lõi sẵn dùng và tải xuống để thực hiện tác vụ hiệu quả hơn so với tự làm bằng thread
Idle-time notificaitons
Đối với tác vụ tương đối ngắn và có mức ưu tiên thấp, Idle-time notificaitons cho phép người lập trình có thể thực hiện nhiệm vụ một thời điểm ứng dụng “nhàn rỗi”. Cocoa sử dụng đối tượng NSNotificationQueue để hỗ trợ idle-time notification. Để yêu cầu một idle-time notification, đăng một thông báo cho đôi tượng mặc định NSNotificationQueue sử dụng tùy chọn NSPostWhenIdle. Hàng đợi sẽ làm trễ việc nhận đối tượng thông báo cho đến khi vòng lặp (run loop) nhàn rỗi.
Asynchronous functions
Các giao diện hệ thống bao gồm nhiều chức năng không đồng bộ cung cấp tính năng xử lý đồng thời tự động. Các API này có thể sử dụng trình nền hệ thống và quy trình hoặc tạo ra các chủ đề tùy chỉnh để thực hiện nhiệm vụ của họ và trả lại kết quả cho bạn. (Việc thực hiện thực tế là không thích hợp bởi vì nó được chia tách từ mã của bạn) Khi bạn thiết kế ứng dụng của bạn, hãy tìm chức năng cung cấp không đồng bộ và xem xét hành vi sử dụng chúng thay vì sử dụng các chức năng tương đương đồng bộ trên một sợi tùy chỉnh.
Timers
Timers có thể được sử dụng trong thread chính của chương trình để thực thi các tác vụ biết trước một cách thường kỳ.
Separate processes
Mặc dù tiêu tốn tài nguyên hơn so với thread, tạo ra một process riêng biệt có thể có ích trong trường hợp tác vụ chỉ liên quan tiếp tuyến đến chương trình. Process có thể được sử dụng cho tác vụ đòi hỏi một số lượng đáng kể bộ nhớ hoặc phải được thực hiện bằng cách sử dụng đặc quyền root.
Bảng 1.5 Các công nghệ có thể sử dụng thay thế thread trong ứng dụng
c. Hỗ trợ thread
* Các loại thread:
Công nghệ
Mô tả
Cocoa threads
Cocoa thực thi thread sử dụng lớp NSThread. Cocoa cũng cung cấp thêm một số phương thức trong lớp NSObject để tạo một thread mới và thực thi code trên thread đã chạy
POSIX threads
POSIX đề cung cấp một giao diện dựa trên C cho việc tạo thread. Đây là lựa chọn tốt nhất dành cho ứng dụng không phải là ứng dụng Cocoa. POSIX tương đối đơn giản để sử dụng và cung cấp sự linh hoạt phong phú cho để cấu hình thread
Muiltiprocessing Services
Các dịch vụ đa xử lý là một giao diện dựa trên C được sử dụng bởi ứng dụng chuyển từ phiên bản cũ của Mac OS. Công nghệ này chỉ có sẵn trong Mac OS X và nên tránh sử dụng. Thay vào đó, bạn nên sử dụng lớp NSThread hoặc POSIX
Bảng 1.6 Các loại thread
Ở cấp độ ứng dụng, tất cả các thread hoạt động về cơ bản giống như trên các nền tảng khác. Sau khi bắt đầu một thread, thread ở trong một trong ba trạng thái chính: chạy, sẵn sàng, hoặc bị chặn. Nếu thread không phải đang chạy, nó hoặc bị chặn và chờ đầu vào hay nó đã sẵn sàng để chạy nhưng chưa được lập lịch để chạy như thế nào. Các thread vẫn tiếp tục thay đổi trạng thái cho đến khi nó thoát ra và chuyển sang trạng thái kết thúc.
*Run Loops:
Run loop là một phần của cơ sở hạ tầng được sử dụng để quản lý sự kiện không đồng bộ trên thread. Một run loop hoạt động bằng cách theo dõi một hoặc nhiều nguồn sự kiện cho thread. Khi các sự kiện xảy ra, hệ thống đánh thức thread và gửi sự kiện tới run loop, mà sau đó gửi cho các bộ quản lý đã được chỉ định. Nếu không có sự kiện có mặt và sẵn sàng để được xử lý, run loop đẩy thread vào trạng thái sleep.
Không phải bất kỳ một thread mới nào được tạo ra cũng phải sử dụng run loop nhưng làm vậy có thể cung cấp một kinh nghiệm tốt hơn cho người sử dụng. Run loop có thể tạo ra thread có thời gian sống dài mà lại dùng số lượng ít nhất tài nguyên. Bởi vì run loop đặt thread của nó vào trạng thái ngủ khi không có gì để làm, nó giúp loại bỏ sự lãng phí CPU cycles và ngăn cho bộ sử lý tự động chuyển sang trạng thái ngủ và tiết kiệm năng lượng.
Để cấu hình một run loop, tất cả những gì phải làm là khởi động thread, có được một tham chiếu đến các đối tượng run loop, cài đặt bộ xử lý sự kiện, và cho run loop chạy. Cơ sở hạ tầng cung cấp bởi cả Cocoa và Carbon xử lý cấu hình của run loop của thread chính tự động. Nếu muốn tạo ra một thread có thời gian sống dài thứ hai cần phải tự cấu hình run loop cho thread đó.
* Các công cụ đồng bộ hóa:
Một trong những nguy cơ của lập trình thread là sự tranh chấp tài nguyện giữa các thread. Nếu nhiều thread cùng sử dụng hoặc sửa đổi cùng một tài nguyên tại cùng một thời gian, các lỗi có thể xảy ra. Một cách để giảm nhẹ lỗi này là loại bỏ các tài nguyên được chia sẻ và chắc chắn rằng mỗi thread được tạo một nguồn tài nguyên riêng để hoạt động. Tuy nhiên việc tạo ra các nguồn tài nguyên riêng cho mỗi thread là không bắt buộc, người lập trình vẫn có thể truy nhập đồng bộ vào các tài nguyên sử dụng kỹ thuật khóa, điều kiện, atomic operation và các kỹ thuật khác.
Khóa giúp bảo vệ cho code chỉ được thực thi bởi duy nhất một thread tại một thời điểm. Loại khóa phổ biến nhất là là khóa loại trừ lẫn nhau, còn được gọi là mutex. Khi một thread cố gắng để có được một mutex hiện tại đang được một thread khác giữ, nó sẽ bị block cho đến khi khóa được thả ra bởi thread kia. Một số các framework hệ thống cung cấp hỗ trợ cho các khóa mutex, mặc dù đều dựa trên công nghệ bên dưới như nhau. Ngoài ra, Cocoa cung cấp một vài biến thể của khóa mutex để hỗ trợ các dạng khác nhau của hành vi, như đệ quy.
Điều kiện bảo đảm đúng trình tự các tác vụ trong ứng dụng. Một điều kiện hoạt động như một “người gác cổng”, ngăn chặn một thread nhất định cho đến khi điều kiện của nó trở thành true. Khi điều đó xảy ra, điều kiện “thả” thread ra và cho phép nó tiếp tục. Lớp POSIX và framework Foundation đều trực tiếp hỗ trợ điều kiện
Mặc dù khóa và điều kiện rất phổ biến trong thiết kế đồng thời, hoạt động nguyên tử là một cách khác để bảo vệ và đồng bộ hóa truy cập dữ liệu. Các hoạt động nguyên tử cung cấp cách thức thay thế cần ít tài nguyên hơn khóa trong các tình huống tính toán toán học hoặc logic trên dữ liệu vô hướng. Các hoạt động nguyên tử sử dụng các hướng dẫn phần cứng đặc biệt để đảm bảo rằng sửa đổi cho một biến được hoàn thành trước khi thread khác có cơ hội truy cập nó.
*Interthread communication
Mặc dù một thiết kế tốt giúp giảm thiểu số lượng giao tiếp cần thiết, trong một số trường hợp, việc giao tiếp giữa các thread trở nên cần thiết. Thread có thể cần phải xử lý yêu cầu công việc mới hoặc báo cáo tiến bộ của mình cho thread của ứng dụng. Trong những tình huống này, cần phải có cachs để có được thông tin từ một thread khác. May mắn thay, thực tế là các thread chia sẻ cùng một không gian xử lý có nghĩa là có nhiều lựa chọn cho giao tiếp giữa các thread.
Có nhiều cách để giao tiếp giữa các thread, mỗi cách có ưu điểm và khuyết điểm riêng của nó. Bảng cơ chế giao tiếp sau chỉ ra những cơ chế giao tiếp giữa các thread thông dụng nhất được sử dụng trong Mac OS X
Cơ chế
Mô tả
Gửi thông điệp trực tiếp
Các ứng dụng Cocoa hỗ trợ khả năng thực hiện selectors trực tiếp từ thread khác. Khả năng này có nghĩa là một thread về cơ bản có thể thực thi một phương thức trên bất kỳ thread khác. Bởi vì chúng đều được thực hiện trong bối cảnh của thread chính, các thông điệp được gửi theo cách này sẽ tự động được serialize trên thread đó.
Biến toàn cục, đối tượng và bộ nhớ chia sẻ
Cách đơn giản khác để truyền thông tin giữa hai thread là sử dụng
Các file đính kèm theo tài liệu này:
- 71677865-Datn-an-Thi-Hong-k50-Cnpm.doc