TRƯỜNG ĐẠI HỌC BÁCH KHOA HÀ NỘI
VIỆN CÔNG NGHỆ THÔNG TIN VÀ TRUYỀN THÔNG
BỘ MÔN KỸ THUẬT MÁY TÍNH
----------
TIỂU LUẬN
MÔN: NGUYÊN LÝ NGÔN NGỮ LẬP TRÌNH
ĐỀ TÀI:
Lập trình song song và lập trình tương tranh (Tìm hiểu
ngôn ngữ lập trình Erlang)
GIẢNG VIÊN
TS. Phạm Đăng Hải
HƯỚNG DẪN:
SINH
VIÊN
1
THỰC HIỆN:
2
LỚP:
3
Hà Nội, tháng 04/2018
Lập trình song song và lập trình tương tranh, Tìm hiểu về ngôn ngữ lập trình
Erlang
MỤC LỤC
Nguyên lý các Ngôn ngữ lập trình
2
Lập trình song song và lập trình tương tranh, Tìm hiểu về ngôn ngữ lập trình
Erlang
1. Lý do chọn đề tài
Công nghệ luôn phát triển không ngừng, những nhu cầu mới phát sinh những công
nghệ mới, những công nghệ nổi bật trở sẽ thành xu hướng. Ngày nay các ứng dụng
thương mại với yêu cầu xử lý một số lượng rất lớn dữ liệu đang là động lực thúc đẩy các
nhà phát triển máy tính và phần mềm tạo ra các máy tính với công nghệ tính toán với tốc
độ ngày một nhanh hơn.
Trong suốt 20 năm qua, xu hướng chỉ ra rằng mạng máy tính ngày càng nhanh hơn,
có nhiều hệ thống phân tán, và các kiến trúc máy tính đa vi xử lý (bao gồm cả máy tính
để bàn) cho thấy rõ ràng song song là tương lai của máy tính.
Công nghệ/Ngôn ngữ lập trình rất quan trọng trong việc xây dựng nên những ứng
dụng chạy trên máy tính điện tử, những ngôn ngữ lập trình cấp cao hiện nay đều hỗ trợ
những phương pháp lập trình mới giúp cho lập trình viên đơn giản hơn trong việc xây
dựng chương trình. Nhưng không chỉ dừng lại ở đó các ngôn ngữ lập trình cũng ngày
càng phát triển, rất nhiều các ngôn ngữ lập trình mới được ra đời, với nhiều đặc trưng
mới phù hợp hơn với các ứng dụng cụ thể, hỗ trợ những paradigm lập trình khác như
parallel, object – oriented,…
Erlang là một ngôn ngữ cấp cao, được ra đời nhằm mục đích xây dựng những ứng
dụng chạy tốt bằng cách song song hóa các yêu cầu tính toán, hỗ trợ lập trình hàm. Một
đặc điểm quan trọng nhất của Erlang đó là đây là ngôn ngữ xây dựng theo hướng lập
trình tương tranh (Concurrency – Oriented), một hướng khá mới, tiểu luận của chúng em
với mục đích tìm hiểu ngôn ngữ lập trình Erlang với các đặc trưng chính của ngôn ngữ và
làm rõ cách xây dựng chương trình theo hướng lập trình tương tranh của Erlang. Sau hơn
20 năm, Erlang có một lượng người dùng lớn bởi vì nó cung cấp các cấu trúc hiệu quả
giúp giảm thiểu những khó khăn khi chạy nhiều luồng song song. Web Server có khả
năng xử lý đồng thời nhiều người dùng một cách chính xác nếu chúng được viết bằng
Erlang, bởi vì ngôn ngữ này được thiết kế để giúp các lập trình viên đưa ra quyết định
hợp lí hơn khi giới hạn các phương pháp xây dựng chương trình.
Nghiên cứu tính toán tương tranh, sử dụng Ngôn ngữ lập trình Erlang là nghiên cứu
một công nghệ tiên phong nhằm giải các bài toán có tính thời sự cao trong thời đại ngày
nay.
Nguyên lý các Ngôn ngữ lập trình
3
Lập trình song song và lập trình tương tranh, Tìm hiểu về ngôn ngữ lập trình
Erlang
2. Phạm vi nghiên cứu
Trong phạm vi nghiên cứu của đề tài này, nhóm nghiên cứu tập trung vào các khái
niệm cơ bản của tính toán song song, trong đó tập trung nghiên cứu lập trình song song
và lập trình tương tranh. Sau đó nhóm tập trung nghiên cứu Ngôn ngữ lập trình Erlang
như một cách cụ thể hóa cách ứng dụng và triển khai một công nghệ cho lĩnh vực này.
Nhóm cũng thực hành việc cài đặt và thực hiện một số ứng dụng thực hành bằng ngôn
ngữ Erlang như một cách kiểm nghiệm các kiến thức đã được học.
Nguyên lý các Ngôn ngữ lập trình
4
Lập trình song song và lập trình tương tranh, Tìm hiểu về ngôn ngữ lập trình
Erlang
DANH MỤC TỪ VIẾT TẮT
STT Ký hiệu, chữ viết tắt
Ý nghĩa
1
CPU
Bộ Vi xử lý
2
Paralell Computing
Tính toán song song
3
Concurrentcy Computing
Tính toán tương tranh
4
Oriented Programming
Lập trình hướng đối tượng
5
Synchronous
Xử lý đồng bộ
6
Asynchronous
Xử lý bất đồng bộ
7
Instruction Set
Tập lệnh
8
9
I. CÁC KHÁI NIỆM
2.1. Tính toán song song (Paralell Computing)
2.1.1. Các khái niệm
Nguyên lý các Ngôn ngữ lập trình
5
Lập trình song song và lập trình tương tranh, Tìm hiểu về ngôn ngữ lập trình
Erlang
a. Synchronous
Synchronous có nghĩa là xử lý đồng
bộ, chương trình sẽ chạy theo từng bước và
chỉ khi nào bước 1 thực hiện xong thì mới
nhảy sang bước 2, khi nào chương trình
này chạy xong mới nhảy qua chương trình
khác. Đây là nguyên tắc cơ bản trong lập
trình mà bạn đã được học đó là khi biên dịch
các đoạn mã thì trình biên dịch sẽ biên dịch
theo thứ tự từ trên xuống dưới, từ trái qua
phải và chỉ khi nào biên dịch xong dòng thứ
nhât mới nhảy sang dòng thứ hai, điều này
sẽ sinh ra một trạng thái ta hay gọi là trạng
thái chờ. Ví dụ trong quy trình sản xuất dây
chuyền công nghiệp được coi là một hệ
thống xử lý đồng bộ.
b. Asynchronous
Ngược lại với Synchronous, Asynchronous là xử lý bất động bộ, nghĩa là chương
trình có thể nhảy đi bỏ qua một bước nào đó, vì vậy Asynchronous được ví như một
chương trình hoạt động không chặt chẽ và không có quy trình nên việc quản lý rất khó
khăn. Nếu một hàm A phải bắt buộc chạy
trước hàm B thì với Asynchronous sẽ không
thể đảm bảo nguyên tắc này luôn đúng.
c. Tính toán song song (Paralell
Computing)
Tính toán song song nói chung là một
hình thức tính toán trong đó nhiều phép tính
được thực hiện đồng thời, hoạt động trên
nguyên tắc là những vấn đề lớn đều có thể
chia thành nhiều phần nhỏ hơn, sau đó được
giải quyết tương tranh ("trong lĩnh vực tính
toán"). Có nhiều hình thức khác nhau của tính
toán song song: song song cấp bit, song song
cấp lệnh, song song dữ liệu, và song song tác
vụ. Song song đã được sử dụng từ nhiều năm qua, chủ yếu là trong lĩnh vực tính toán
Nguyên lý các Ngôn ngữ lập trình
6
Lập trình song song và lập trình tương tranh, Tìm hiểu về ngôn ngữ lập trình
Erlang
hiệu năng cao. Gần đây hình thức tính toán này được quan tâm nhiều hơn, do những hạn
chế vật lý ngăn chặn việc tăng hiệu năng tính toán chỉ bằng cách tăng tần số. Vì việc tiêu
hao điện năng (dẫn đến sinh nhiệt) từ máy tính đã trở thành một mối lo ngại trong những
năm gần đây, tính toán song song đã trở thành mô hình thống trị trong lĩnh vực kiến trúc
máy tính, phần lớn là dưới dạng bộ xử lý đa nhân.
d. Tương tranh (Concurrent)
Trong ngành khoa học máy tính, tương tranh là một tính chất của các hệ thống bao
gồm các tính toán được thực thi trùng nhau về mặt thời gian, trong đó các tính toán chạy
đồng thời có thể chia sẻ các tài nguyên dùng chung. Hoặc theo lời của Edsger Dijkstra:
"Tương tranh xảy ra khi nhiều hơn một luồng thực thi có thể chạy đồng thời."Việc cùng
sử dụng các tài nguyên dùng chung, chẳng hạn bộ nhớ hay file dữ liệu trên đĩa cứng, là
nguồn gốc của nhiều khó khăn. Các tranh đoạt điều khiển (race condition) liên quan đến
các tài nguyên dùng chung có thể dẫn đến ứng xử không đoán trước được của hệ thống.
Việc sử dụng cơ chế loại trừ lẫn nhau (mutual exclusion) có thể ngăn chặn các tình huống
chạy đua, nhưng có thể dẫn đến các vấn đề như tình trạng bế tắc (deadlock) và đói tài
nguyên (resource starvation). Thiết kế của các hệ thống tương tranh thường là kết quả của
việc tìm kiếm các kĩ thuật đáng tin cậy cho việc phối hợp hoạt động của thực thi, trao đổi
dữ liệu, cấp phát bộ nhớ và lập lịch thực thi để giảm tối thiểu thời gian phản ứng
(response time) và tăng tối đa thông lượng (throughput).
2.1.2. Các lý do chính của việc tính toán song song
Những lý do chính cho việc sử dụng tính toán song song:
•
Tiết kiệm thời gian
•
Giải quyết những bài toán lớn
•
Xử lý đồng thời cùng một lúc
Ngoài ra, còn có một số lý do khác
•
Tận dụng các nguồn tài nguyên như khai thác tài nguyên tính toán có sẵn trên
mạng diện rộng, hoặc thậm chí sử dụng Internet khi các tài nguyên cục bộ hạn
chế
•
Tiết kiệm chi phí – sử dụng nhiều tài nguyên máy tính “rẻ” thay vì phải đầu tư
một con siêu máy tính.
Nguyên lý các Ngôn ngữ lập trình
7
Lập trình song song và lập trình tương tranh, Tìm hiểu về ngôn ngữ lập trình
Erlang
•
Khắc phục những hạn chế về bộ nhớ - Các máy tính đơn có tài nguyên bộ nhớ
rất hữu hạn. Đối với những bài toán lớn, sử dụng bộ nhớ của nhiều máy tính có
thể vượt qua trở ngại này.
2.1.3. Các đơn vị xử lý chính trong tính toán song song
•
Processes
•
Threads
2.2. Lập trình song song (Paralell Programming)
2.2.1. Khái niệm
Lập trình song song (Parallel Computing) là việc chia một công việc ra thành các
công việc nhỏ và cho các công việc này thực hiện đồng thời với nhau bởi các hệ thống có
nhiều bộ vi xử lý (multiprocessor) hay bộ vi xử lý đa nhân (multicore) nhằm giảm thời
gian thực hiện công việc đó xuống. Việc lập trình để tách ra các công việc nhỏ và sắp xếp
để xử lý song song được gọi là lập trình song song.
Cùng với định luật Moore, các máy tính hiện nay được được trang bị các bộ vi xử lý
đa nhân mạnh mẽ. Tuy nhiên, để tận dụng được sức mạnh đó đòi hỏi các lập trình viên
phải tận dụng được hết các nhân trong bộ vi xử lý. Vì vậy, thay vì lập trình tuần tự như
trước đây chỉ sử dụng một nhân của bộ vi xử lý thì người lập trình viên ngày nay phải
dùng kỹ thuật lập trình song song để tận dụng hiệu suất của bộ vi xử lý đa nhân.
Bộ vi xử lý có nhiều nhân sẽ tăng tốc chương trình song song tuy nhiên không có
nghĩa là nó sẽ tăng lên 100% trên một nhân (core) được thêm vào. Thậm chí chương tr.nh
song song không hề tăng hiệu suất lên trong một số trường hợp. Vì vậy lập trình viên phải
biết quyết định khi nào sử dụng lập trình song song bằng cách sử dụng các công cụ đo
lường để xác định được tốc độ thực thi của chương trình.
Lập trình song song là một công việc rất phức tạp so với lập trình tuần tự thông
thường, người phát triển phải thực hiện một quá trình “song song hóa”, biến đổi các
chương trình tuần tự thành chương trình song song có khả năng tận dụng tối đa sức mạnh
của hệ thống.
Nguyên lý các Ngôn ngữ lập trình
8
Lập trình song song và lập trình tương tranh, Tìm hiểu về ngôn ngữ lập trình
Erlang
2.2.2. Các thành phần
Quá trình song song hóa gồm ba giai đoạn bước chính
•
Phân chia chương trình thành các công việc con (Sub-task decomposition).
•
Phân tích sự phụ thuộc (Dependence analysic).
•
Định thời các công việc (Task scheduling).
2.3. Lập trình tương tranh (Concurrent Programming)
2.3.1. Khái niệm
Lập trình tương tranh (Concurrent Programming) là một khái niệm (cũng có thể
hiểu là kỹ thuật) lập trình chia nhỏ công việc ban đầu thành những nhiệm vụ (task) nhỏ
hơn, mỗi nhiệm vụ chạy trên một tiến trình (process, hay thread), và chúng có thể chạy
đồng thời (song song - parallel) trên nhiều CPU
2.3.2. Đơn vị xử lý Tiến trình (Process)
Là một khái niệm trừu tượng của Hệ điều hành, biểu hiện một chương trình
(program) đang chạy Process là quá trình hoạt động của một ứng dụng. Điều này có
nghĩa là process bao gồm các dòng lệnh và trạng thái thực thi của những dòng lệnh đó. Ví
dụ khi bạn click đúp vào icon Microsoft Word trên màn hình desktop để khởi động
chương trình chính là bạn đã bắt đầu một process của chương trình Word. Khi process bắt
đầu thì một vùng nhớ sẽ được phân phối để sử dụng trong quá trình thực thi instruction.
Hệ điều hành (OS) ngày nay cho phép phân phối nhiều process cùng một lúc lên bộ
nhớ để thực hiện đồng thời nhiều tác vụ (multitasking). Thực tế thì những process này
Nguyên lý các Ngôn ngữ lập trình
9
Lập trình song song và lập trình tương tranh, Tìm hiểu về ngôn ngữ lập trình
Erlang
không được thực thi đồng thời mà sẽ được phân bổ qua một bộ phận của OS gọi là
scheduler. Bạn có thể tưởng tượng bộ phận này như một công tắc chuyển đổi để lần lượt
đưa các chương trình này vào CPU. Bộ phận này sẽ thay đổi qua lại giữa các chương
trình liên tục trong khoảng thời gian rất ngắn, giả sử là 1 mili giây. Giả sử mỗi mili giây
sẽ có khoảng 1000 instruction được thực thi thì đối với con người là quá nhanh và làm
chúng ta tưởng như là 2 chương trình đang chạy đồng thời (concurrent).
2.3.3. Đơn vị xử lý Luồng (Threads)
Đa số chương trình ngày nay hỗ trợ đa luồng (multithread). Ví dụ như một process
của chương trình Word có thể có 1 luồng (thread) để nhận chỉ thị từ người dùng qua bàn
phím và hiển thị lên màn hình, 1 thread để thực hiện chức năng in tài liệu ở background,
1 thread được dùng để lưu xuống ổ cứng.
Có thể tưởng tượng thread là một phiên bản nhẹ hơn của process. Một thread có thể
thực hiện bất cứ tác vụ gì mà một process có thể thực hiện. Tuy vậy, do thread nằm trong
process, những tác vụ của thread thực hiện thường nhỏ hơn. Điểm khác biệt cơ bản giữa
thread và process là nhiều thread nằm trong cùng một process dùng một không gian bộ
nhớ giống nhau, trong khi process thì được phân bổ riêng biệt. Điều này cho phép các
thread đọc và viết cùng một kiểu cấu trúc và dữ liệu, giao tiếp dễ dàng giữa các thread
với nhau. Việc có nhiều thead chạy song song trong cùng một process giúp multitasking
nhẹ và dễ dàng hơn.
Dưới đây là bảng so sánh giữa process và thread:
Processes
Sử dụng nhiều bộ nhớ
Threads
Sử dụng ít bộ nhớ hơn
Nếu process cha ngưng trước khi process con
Tất cả threads sẽ ngừng hoạt động khi
thoát thì process con sẽ vẫn tồn tại nhưng
process ngừng hoạt động
không hoạt động
Sử dụng nhiều tài nguyên hơn vì phải OS phải Sử dụng ít tài nguyên hơn vì dùng chung
lưu và reload mỗi khi chuyển đổi giữa process không gian và bộ nhớ
Process được tạo mới được cung cấp cho một Thread chia sẻ bộ nhớ nên cần phải giải
không gian bộ nhớ ảo (process isolation)
quyết vấn đề chia sẻ tài nguyên cho hợp lý
Đòi hỏi giao tiếp giữa process với nhau
Có thể giao tiếp qua queue và bộ nhớ
chung
Tạo và xóa chậm hơn
Tạo và xóa nhanh hơn
Dễ dàng code và debug hơn
Có thể vô cùng phức tạp để code và debug
Nguyên lý các Ngôn ngữ lập trình
10
Lập trình song song và lập trình tương tranh, Tìm hiểu về ngôn ngữ lập trình
Erlang
Processes
Mỗi process có code, data và cấu trúc kernel
riêng biệt
Threads
Thread chia sẻ code, data và cấu trúc
kernel trong cùng 1 process
Thread là đơn vị nhỏ nhất trong Lập trình tương tranh (Concurrent programming).
Một trong những đặc tính cơ bản nhất là chúng giúp chúng ta có thể thực thi nhiều công
việc (task) trong cùng một lúc.
•
Một Process chứa một hoặc nhiều luồng (Thread).
•
Các Thread có Tập thanh ghi (Register Set) và ngăn xếp (Stack) riêng
•
Các Threads trong một Process chia sẻ bộ nhớ với nhau (Các Threads cũng có
thể có bộ nhớ riêng)
Trong mô hình lập trình tương tranh theo mô hình thread, một xử lý đơn có thể
chuyển thành đa xử lý bằng cách thực thi đồng thời theo các cách khác nhau. Đơn giản
nhất để mô tả mô hình thread, chúng ta phân tích ví dụ trên với một số thủ tục trong
chương trình:
Nguyên lý các Ngôn ngữ lập trình
11
Lập trình song song và lập trình tương tranh, Tìm hiểu về ngôn ngữ lập trình
Erlang
•
Chương trình chính a.out được lập lịch để chạy trên hệ điều hành. a.out nạp và
yêu cầu hệ thống và các tài nguyên cần thiết để chạy chương trình.
•
a.out thực thi một dãy các công việc, và rồi tạo ra một dãy các tác vụ (các
thread) mà có thể được lập lịch và chạy đồng thời bởi hệ điều hành.
•
Mỗi thread có dữ liệu cục bộ, nhưng cũng chia sẻ toàn bộ tài nguyên của a.out.
Điều này tiết kiệm các chi phí liêu quan đến tái tạo tài nguyên cuả chương trình
cho mỗi thread. Mỗi thread cũng có quyền với bộ nhớ toàn cục vì nó chia sẻ
không gian bộ nhớ của a.out.
•
Công việc của thread có thể được miêu tả tốt như một chương trình con trong
chương trình chính. Bất kỳ thread nào cũng có thể thực thi bất kỳ chương trình
con tại cùng thời điểm như các thread khác.
•
Các thread giao tiếp với nhau qua bộ nhớ toàn cục (cập nhật vị trí địa chỉ). Điều
này đòi hỏi các cấu trúc đồng bộ để đảm bảo rằng khi có nhiều hơn một thread
thì sẽ không cập nhật lên cùng địa chỉ toàn cục tại cùng thời điểm.
•
Các thread có thể chạy luân phiên nhau, nhưng a.out vẫn giữ lại trạng thái hiện
tại để cung cấp các tài nguyên chia sẻ khác cho đến khi ứng dụng kết thúc.
•
Các threadthường được kết hợp với các kiến trúc bộ nhớ chia sẻ và hệ điều
hành.
Nguyên lý các Ngôn ngữ lập trình
12
Lập trình song song và lập trình tương tranh, Tìm hiểu về ngôn ngữ lập trình
Erlang
2.4. Lập trình song song vs Lập trình tương tranh (Paralell Programming
vs Concurrent Programming)
II. ERLANG VÀ HỆ THỐNG HỖ TRỢ LẬP TRÌNH VỚI
ERLANG
2.1.Giới thiệu chung
Erlang là một ngôn ngữ lập trình nhằm mục đích lập trình tương tranh cho phép
thích ứng trên nhiều nền tảng khác nhau. Với Erlang, sự song song hóa chỉ phụ thuộc vào
ngôn ngữ lập trình chứ không phụ thuộc vào hệ điều hành.
Erlang thực hiện cấu trúc chương trình thành các tiến trình tương tranh (concurrent
process), từ đó khiến cho việc lập trình song song trở nên dễ dàng bằng cách thông qua
các thông điệp được gửi qua lại giữa các tiến trình tương tranh này. Các tiến trình tương
tranh này chạy song song nhưng không dựa trên một quá trình chia sẻ bộ nhớ vì vậy
không gây ra hiện tượng đợi lẫn nhau giữa các tiến trình.Các tiến trình tương tranh này
nhỏ và nhẹ vì vậy mỗi chương trình bao gốm một số lượng lớn các tiến trình tương tranh
(lên đến hàng trăm, hàng nghìn) có thể được thực thi trên một bộ xử lý, một bộ xử lý đa
lõi, hoặc trên một mạng các bộ xử lý khác nhau.
Nguyên lý các Ngôn ngữ lập trình
13
Lập trình song song và lập trình tương tranh, Tìm hiểu về ngôn ngữ lập trình
Erlang
Có 5 lý do mà tác giả của ngôn ngữ khuyên chúng ta nên biết về nó :
•
Chương trình sẽ chạy nhanh hơn khi được thực thi trên một máy tính đa lõi.
•
Khả năng xây dựng ứng dụng có chịu lỗi tốt, chúng có thể được chỉnh sửa
mà không cần phải dừng ứng dụng lại.
•
Ngôn ngữ sử dụng paradigm lập trình hàm (functional programming).
•
Đây là một ngôn ngữ được kiểm nghiệm thực tế trong hệ thống sản xuất
công nghiệp quy mô lớn, có các thư viện khổng lồ, và cộng đồng sử dụng rất
lớn.
•
Các ứng dụng được viết với lượng dòng lệnh không quá nhiều.
2.2.Lịch sử của Erlang
Erlang được phát triển tại phòng thí nghiệm Ericsson Computer Science Laboratory
vào những năm 1986. Erlang được thiết kế cho việc lập trình tương tranh, một chương
trình trong Erlang được tạo thành bởi hàng nghìn, hàng vạn các tiến trình, các tiến trình
này không chia sẻ bộ nhớ và giao tiếp với nhau qua thông qua các message. Erlang có cơ
chế cho phép chương trình có thể thay đổi code ngay khi chúng đang được thực thi, ta gọi
đó là cơ chế “on the fly”, chính nhờ cơ chế này ta có thể xây dựng các phần mềm cho các
hệ thống non-stop.
2.2.1.Giai đoạn 1985-1988 : Sự ra đời của Erlang:
Vào những năm 1986, Joe Armstrong dự định phát triển một trình biên dịch cho
phép lập trình các ứng dụng trên điện thoại một cách hiệu quả. Như chúng ta đã biết, các
ứng dụng trên điện thoại có yêu cầu về khả năng tương tranh cao: một thao tác switch
phải xử lý hàng trăm thậm chí hàng nghìn các giao dịch. Trong quá trình phát triển, ông
bị ảnh hưởng khá nhiều bởi cấu trúc cú pháp của ngôn ngữ Prolog. Trình biên dịch của
ông không những hỗ trợ cho nhiều process cũng lúc mà còn có cơ chế trao đổi thông điệp
giữa các process, cơ chế bắt lỗi … Cùng với Robert Virding, ông đã nghiên cứu và phát
triển ra một ngôn ngữ lập trình, đồng thời phát triển các nguyên lý cho ngôn ngữ này,
ngày này chúng ta gọi ngôn ngữ này là Erlang và nguyên lý mà hai người phát triển là
“Concurrency-Oriented Programming”. Như vậy Erlang là một ngôn ngữ lai giữa ngôn
ngữ lập trình tương tranh và ngôn ngữ lập trình hàm.
Nguyên lý các Ngôn ngữ lập trình
14
Lập trình song song và lập trình tương tranh, Tìm hiểu về ngôn ngữ lập trình
Erlang
Hình 1. Cấu trúc của ngôn ngữ Erlang
Vào những năm 1987, Erlang lần đầu tiên được sử dụng để thực hiện các ứng dụng
thực tế, Erlang được sử dụng để tạo ra một kiến trúc phần mềm mới mang tên là ACS3
được thiết kế cho lập trình các dịch vụ điện thoại trên Ericsson MD110 PABX4, và dự án
mang tên ACS/Dunder đã được triển khai, dựa trên ngôn ngữ erlang để xây dựng kiến
trúc ACS3. Chính nhờ dự án này, ngôn ngữ erlang được phát triển một cách nhanh chóng,
erlang liên tục được cập nhật thêm các tính năng mới, và được áp dụng ngay vào trong
thực tế, những tính năng phù hợp sẽ được giữ lại, còn không chúng sẽ được bỏ đi. Hầu
hết các thay đổi của Erlang đều không được lưu lại, bảng dưới đây là một trong những
mẫu câu lệnh hiếm hoi của erlang trong những ngày đầu phát triển
Nguyên lý các Ngôn ngữ lập trình
15
Lập trình song song và lập trình tương tranh, Tìm hiểu về ngôn ngữ lập trình
Erlang
Hình 2. Các mẫu câu lệnh của Erlang trong những ngày đầu
Đến cuối năm 1988, hầu hết mọi ý tưởng trong erlang đã định hình:
• Bufered message reception: đây là một trong những ý tưởng được thiết kế
đầu tiên trong erlang, cú pháp gửi nhận tin nhắn được thực hiện theo mẫu
sau :
Receive
Patten1 ->
Action1;
Patten2 ->
Action2;
…;
End
• Error handling: xử lý lỗi trong erlang khác rất nhiều so với xử lý lỗi trong
những ngôn ngữ lập trình thông thường, cơ chế xử lý lỗi trong erlang được
thiết kế để xây dựng lên một hệ thống chịu lỗi, chúng ta không thể xây dựng
một hệ thống chịu lỗi với một máy tính, điều kiện tiên quyết để xây dựng
một hệ thống chịu lỗi là hệ thống phải có hai máy tính trở lên, quá trình phát
hiện lỗi và phục hồi sẽ được thực hiện trên máy còn lại bởi vì trong trường
hợp xấu nhất, máy tính bị lỗi sẽ bị treo và không thể tính toán được nữa dẫn
đến không thể phục hồi. Trong erlang, mọi thiết bị phần cứng được coi như
Nguyên lý các Ngôn ngữ lập trình
16
Lập trình song song và lập trình tương tranh, Tìm hiểu về ngôn ngữ lập trình
Erlang
một đối tượng, và muốn khi muốn tương tác đến phần cứng thì phải thông
qua việc gửi tin nhắn. Điều này khiến cho mọi việc trở lên dễ dàng trong
erlang vì tất cả đều được quy về việc xử lý các process, và việc trao đổi tin
nhắn giữa các process là đồng nhất chứ không phân biết xem tin nhắn nào là
tin nhắn gửi đến thiết bị phần cứng, tin nhắn nào là tin nhắn giữa các process
với nhau.
• Links: links trong erlang dùng để kiểm soát đường truyền lỗi khi xảy ra lỗi
giữa các process. Mô hình xử lý lỗi trong erlang như sau: khi một process
trong erlang “chết”, sẽ có một số các process đóng vai trò “quan sát viên”,
chúng theo dõi cái chết của process đó và sửa những lỗi gây ra cái chết của
process kia, tuy nhiên vậy thi process nào trong hệ thống sẽ được đóng vai
trò làm “quan sát viên” trong số hàng trăm, hàng nghìn process trong một
chương trình của erlang. Chính nhờ links trong erlang mà chúng ta có thể tạo
ra một tập các process được liên kết với nhau, và khi một process thông
thường chết thi các process liên kết với nó sẽ chết theo, tuy nhiên trong số
chúng sẽ có những process được gán làm process hệ thống, khi các process
thông thường chết, chúng sẽ có nhiệm vụ khắc phục lỗi. Ý tưởng này do
Mike Williams đề xuất.
• Buffers: như ta đã biết các process trong erlang giao tiếp với nhau thông qua
việc gửi thông điệp đến nhau, mỗi một process đều có một “mailbox” để
chứa các thông điệp. Khi một thông điệp được gửi đến nó được đưa vào
mailbox, và process sẽ lên kế hoạch để thực hiện nó. Nếu process thực hiện
đối sánh mẫu thông điệp trong mailbox, nếu thành công thông điệp sẽ được
xóa khỏi mailbox và dữ liệu từ thông điệp sẽ được đưa vào chương trình, nếu
không thông điệp sẽ được đưa vào một hàng đợi. Khi bất cứ một thông điệp
nào được process xử lý tiếp theo, thì những thông điệp được lưu trong hàng
đợi sẽ được đưa lại vào mailbox.
2.2.2.Giai đoạn 1989-1997
Trong khoảng thời gian 8 năm này chính là giai đoạn phát triển chính của Erlang, từ
thời gian ban đầu chỉ có hai người phát triển, đến giai đoạn này đã có hàng trăm người
nghiên cứu và sử dụng erlang.
Nguyên lý các Ngôn ngữ lập trình
17
Lập trình song song và lập trình tương tranh, Tìm hiểu về ngôn ngữ lập trình
Erlang
Kết quả của dự án ACS/Dunder: vào tháng 12 năm 1989, báo cáo cuối cùng của dự
án ACS/Dunder được đưa ra, đã có khoảng 25 tính năng của điện thoại đã được thực hiện
thành công, các tính năng này đại diện cho khoảng một phần mười chức năng của
MD110. Báo cáo cũng chỉ ra rằng, Erlang quá chậm để phát triển sản phẩm, để có thể tạo
ra các sản phẩm thực tế thì erlang cần phải nhanh hơn 40 lần so với hiện tại, tuy nhiên
Ericsson vẫn quyết định xây dựng một sản phẩm gọi là “Mobility Server” dựa trên kiến
trúc ACS/Dunder.
Mở rộng ra thế giới: năm 1989 cũng là lần đầu tiên Erlang có cơ hội được giới thiệu
ra ngoài thế giới thông qua hội nghị SETSS tại Bournemouth. Chính nhờ hội nghị này,
mà các tác giả đã được mởi đến Bellcore để nói về Erlang.
2.3.Hệ thống hỗ trợ lập trình Erlang
Hệ thống hộ trợ lập trình của Erlang (trình dịch và các thư viện liên quan) được
cung cấp miễn phí tại trang web />Tại đây ta có thể chọn nền hệ điều hành của máy tính thực thi là Linux hay Window.
Ở đây để tiện cho việc cài đặt và thực thi chương trình em chọn Window.
Ngoài ra chúng ta cần lưu ý đến CEAN (The Comprehensive Erlang Archive
Network) đây là sự tổng hợp tất cả các ứng dụng quan trọng của Erlang vào một bộ cài
đặt duy nhất, điều này giúp cho ta có thể khai thác được một lượng lớn các gói chương
trình được viết bằng Erlang. Để có thể sử dụng CEAN, ta truy cập vào đường dẫn
/>Việc thực hiện cài đặt chương trình khá đơn giản, sau khi cài đặt xong chương trình
ta có thể thấy có ba chương trình thực thi phục vụ cho việc lập trình được gọi là shell
trong thư mục bin đó là:
• erl.exe đây là shell để chạy trên dòng lệnh command line. Nếu không có
tham số đi kèm, hệ thống tự động chạy vào command mode của erlang.
• erlc.exe đây là shell để compile các file .erl ra file đã được biên dịch
.beam. Các file này có thể chạy trực tiếp trong môi trường command mode
của erlang.
• werl.exe đây là shell để chạy thông qua cứa sổ của window khi nháy kép
vào file chạy ta sẽ thấy xuất hiện cửa sổ ứng dụng của shell
Nguyên lý các Ngôn ngữ lập trình
18
Lập trình song song và lập trình tương tranh, Tìm hiểu về ngôn ngữ lập trình
Erlang
Dưới đây là shell chạy qua cửa sổ và dòng lệnh chạy thử:
Hình 3. Shell cửa sổ của Erlang và lệnh tính biểu thức 2 * (3 + 4)
Từ hình trên ta có thể nhận ra một số thông tin về version của Erlang cũng như
version của Eshell. Shell được thực hiện với dấu nhắc 1>. Để thực hiện biểu thức đã cho
ta gõ trực tiếp biểu thức sau dấu nhắc của hệ thống. Một lỗi khá gặp đối với người mới
lập trình Erlang đó là dấu “.” cuối cùng sau biểu thức. Dấu chấm này đánh dấu kết thúc
một dòng lệnh để chuyển sang lệnh kế tiếp (dấu nhắc hệ thống chuyển thành 2>), nếu
thiếu dấu chấm này câu lệnh sẽ không được thực hiện và nếu ta ấn ENTER dấu nhắc của
hệ thống vẫn là 1> mà không đưa ra được kết quả của biểu thức.
2.3.1.Viết code bằng file – module
Để tiện cho quá trình lập trình cũng nhưng thêm các tham số cho quá trình biên dịch
Erlang hỗ trợ xây dựng các Module.
Modules: là đơn vị cơ bản trong Erlang, toàn bộ các functions mà chúng ta viết
được chứa trong modules, các modules được lưu trong các file có phần mở rộng .erl.
Modules cần được biên dịch trước khi chạy. Một file modules đã được biên dịch sẽ có
phần mở rộng là .beam. Xét ví dụ sau:
1>Rectangle = {rectangle, 10,
{rectangle, 10, 5}.
2>Circle = {circle, 2.4}.
Nguyên lý các Ngôn ngữ lập trình
5}.
19
Lập trình song song và lập trình tương tranh, Tìm hiểu về ngôn ngữ lập trình
Erlang
{circle,2.40000}
3>{rectangle, Width, Ht} = Rectangle.
{rectangle,10,5}
4>Width.
10
5>Ht.
5
6>{circle, R} = Circle.
{circle,2.40000}
7>R.
2.40000
Ta khởi tạo hai tuple đại diện cho hình chữ nhật với các kích thước là 10 và 5, và
hình tròn với bán kính là 2.4. Bây giờ ta sẽ tạo ra một function với tên gọi là area dùng
để tính chu vi của hình chữ nhật và hình tròn. Chúng ta sẽ để function này trong module
mang tên là geometry và được lưu trong file geometry.erl :
-module(geometry).
-export([area/1]).
area({rectangle, Width, Ht}) -> Width * Ht;
area({circle, R}) -> 3.14159 * R * R.
Như vậy, ta thấy rằng phần code của function area bao gồm hai mệnh đề được cách
nhau bằng dẫu “ ; “ và mệnh đề cuối cùng kết thúc bằng dấu chấm. mỗi mệnh đề có phần
đầu và phần thân, phần đầu bao gồm tên function và đi theo nó là pattern (trong ngoặc
đơn), phần thân bao gồm các chuỗi các biểu thức sẽ được tính toán nếu các thông số ở
pattern phù hợp. Để có thể chạy được chương trình trên ta cần biên dịch nó:
1>c(geometry).
{ok,geometry}
2>geometry:area({rectangle, 10, 5}).
50
3> geometry:area({circle, 1.4}).
6.15752
Ở câu lệnh thứ 1 c(geometry) dùng để biên dịch mã code trong file
geometry.erl, trình biên dịch trả lại kết quả thành công {ok, geometry}, ở câu lệnh
thứ 2 và 3 ta gọi functiong trong module geometry. Khi ta gọi câu lệnh
geometry:area({rectangle, 10, 5}), thì việc đầu tiên đó là gán giá trị
Width = 10 và Ht = 5, sau đó phần code sau dấu -> được thực hiện đó là lấy
Width*Ht.
Nguyên lý các Ngôn ngữ lập trình
20
Lập trình song song và lập trình tương tranh, Tìm hiểu về ngôn ngữ lập trình
Erlang
III. HỆ THỐNG TỪ VỰNG CỦA ERLANG
3.1.Hệ thống ký tự của ngôn ngữ Erlang
Trong Erlang hệ thống ký tự rất đa dạng Erlang có thể nhận được khá nhiều ký tự
(tất cả ký tự của bảng ISO-8859-1 (Latin-1).
Các ký tự được phân loại như dưới bảng sau:
Mã cơ số 8
200 - 237
240 - 277
300 - 326
327
330 - 336
337 - 366
367
370 - 377
Mã thập phân
128 - 159
160 - 191
192 - 214
215
216 - 222
223 - 246
247
248 - 255
Ký tự
-¿
À-Ö
×
Ø-Þ
ß-ö
÷
ø-ÿ
Thuộc lớp
Các ký tự điều khiển
Dấu câu
Chữ in hoa
Dấu câu
Chữ in hoa
Chữ thường
Dấu câu
Chữ thường
3.2.Các từ khóa của Erlang (reserved word):
after and andalso band begin bnot bor bsl bsr bxor case catch cond
div end fun if let not of or orelse query receive rem try when xor
3.3.Biến và các atom hợp lệ
Các cụm ký tự được bắt đầu bởi chữ cái in hoa được coi là một biến, mỗi biến được
bắt đầu bằng _ là một biến có thể đại diện cho bất kỳ giá trị nào (ta không quan tâm và
không tác động đến biến dạng này).
Các cụm ký tự mà không bắt đầu được coi như một atom, nếu muốn có atom bắt
đầu bằng chữ cái in hoa ta để trong dấu nháy đơn (‘’).
3.4.Dấu phân cách
• Hằng ký tự nằm trong cặp nháy kép (“”)
• Dấu « . » là thể hiện kết thúc lệnh sau dấu này ấn Enter sẽ thực hiện
• Dấu « , » là dấu thể hiện phân tách giữa các phần tử trong 1 list, 1 danh sách
tham số, …
• Dầu « ; » là dấu thể hiện các vế của một câu lệnh.
Nguyên lý các Ngôn ngữ lập trình
21
Lập trình song song và lập trình tương tranh, Tìm hiểu về ngôn ngữ lập trình
Erlang
• Đằng sau dấu % là một câu chú thích, toàn bộ những kí tự đằng sau dấu %
cho đến cuối dòng đều được coi là câu chú thích và sẽ không được dịch bởi
Erlang compiler.
IV. KIỂU DỮ LIỆU CƠ BẢN CỦA ERLANG
4.1.Biến - Variable
Đối với một ngôn ngữ lập trình thì biến là một thành phần rất quan trọng của ngôn
ngữ nó là nơi thể hiện cách xử lý đối với vùng nhớ của một ngôn ngữ lập trình, tùy từng
ngôn ngữ mà các biến có thể được xử lý khác nhau, chúng được gán cho một tập các kiểu
tương ứng ở từng ngôn ngữ lập trình. Mỗi biến được gán cho một tên để người lập trình
có thể thao tác với biến thông qua tên
Trong Erlang thì tên của biến được thể hiện bằng một cụm các ký tự bắt đầu bằng
một chữ cái in hoa. Khi thực thi chương trình chạy trên shell mỗi thành phần đều được
gán với một biến cụ thể.
Biễn số thường được khai báo dưới dạng bắt đầu bằng chữ cái in hoa hay là dấu
gạch dưới và có thể bao gồm các ký tự số và chữ, gạch chân và @. Ví dụ:
X
Name1
PhoneNumber
Phone_number
_
_Height
Các biến số được ràng buộc tới giá trị đang sử dụng. Erlang chỉ cho phép 1 biến số
chỉ đuợc ràng buộc 1 lần.
Những biến số bất định được biểu diễn bởi dấu gạch dưới (_) và có thể được sử
dụng khi một biến được sử dụng, nhưng giá trị của nó bị bỏ qua. Ví dụ:
[H | _] = [1, 2, 3]
Các biến số bắt đầu với dấu gạch chân (_Height) là các biến số ở dưới dang thường,
không phải là dạng bất định, Tuy nhiên chúng được bỏ qua bởi chương trình dịch, chúng
sẽ không sinh ra bất kỳ các cảnh bảo nào cho nhưng biến không được sử dụng. Ví dụ:
member(_,[]) -> [].
Có thể được viết để dễ dàng đọc hơn:
Nguyên lý các Ngôn ngữ lập trình
22
Lập trình song song và lập trình tương tranh, Tìm hiểu về ngôn ngữ lập trình
Erlang
member(Elem, []) -> [].
Tuy nhiên điều này sẽ gây nên nhưng cảnh báo cho các biến số không đuợc sử dụng
Elem, nếu code được biên dịch với cờ warn_usused_vars. Chúng ta có thể viết lại dưới
dạng sau:
Member(_Elem,[]) -> [].
Chú ý ràng các biến số bất đầu với dấu gạch chấn sẽ phù hợp (match) với:
{_, _} = {1, 2}
Nhưng sẽ gây lỗi:
{_N, _N} = {1, 2}
Phạm vi của biến này là trong mệnh để function của nó (function clause). Các biến
được ràng buộ trong một nhánh của các lệnh if, case, hay các biểu thức nhận về phải
được ràng buộc trong tất các các nhánh để có thể nhần được giá trị bên ngoài biểu thức,
học không chúng sẽ được lưu ý dưới dạng unsafe (không an toàn) bên ngoài biểu thức.
Mỗi biến trong Erlang được dùng một phép gán (“=”) một lần duy nhất khi muốn
biết giá trị của biến nào thì chỉ cần gõ tên biến ra. Một khi X đã có giá trị cố định ta có
thể sử dụng nó, tuy nhiên khi ta có ý định gán lại cho X một giá trị khác thì shell sẽ báo
lỗi, bởi vì thứ nhất biến trong Erlang không phải là một biến như ta vẫn quan niệm như
trong các ngôn ngữ lập trình khác như Java hay C, thứ hai dấu “=” không phải là toán tử
gán. Các biến trong Erlang có giá trị không thay đổi, ta gọi chúng là các “single
assignment variables”, các biến này chỉ nhận giá trị một lần duy nhất, các biến đã được
nhận giá trị thì được gọi là “bound variable” còn các biến chưa được nhận giá trị thì được
gọi là “unbound variable”, tất cả các biến ban đầu đều thuộc dạng “unbound variable”.
Khi Erlang nhận được một câu lệnh X=1234 thì nó sẽ liên kết biến X với giá trị
1234, và trước khi được gán trị 1234 thì biến X có thể nhận bất cứ giá trị nào, tuy nhiên
khi đã được gán thì nó sẽ giữ giá trị đó mãi mãi.
Trong Erlang thì phép gán “=” được thực hiện là một phép “pattern matching
operation”, ta có thể gọi tắt là phép bound. Mỗi khi thực hiện phép bound Lhs = Rhs,
thì Rhs sẽ được thực hiện tính toán trước, sau đó kết quả này được đối sánh với Lhs, nếu
không thực hiện được lỗi sẽ được đưa ra
Ví dụ:
1> X.
** 1: variable 'X' is unbound **
2> X = 2.
2
Nguyên lý các Ngôn ngữ lập trình
23
Lập trình song song và lập trình tương tranh, Tìm hiểu về ngôn ngữ lập trình
Erlang
3> X + 1.
3
4> {X, Y} = {1, 2}.
** exception error: no match of right hand side value {1,2}
5> {X, Y} = {2, 3}.
{2,3}
6> Y.
3
Trong khi thực hiện Shell ta có thể dùng lênh f() để cho phép shell quên đi các phép
bound trước đó đến các biến khi đó ta có thể thực hiện bound lại một tên biến đã sử dụng.
4.2.DataType của Erlang
4.2.1.Số và biểu diễn số trong Erlang
Các số được cung cấp các cách viết thông thường với các số nguyên và các số thực
(các số thực được viết dưới dạng dấu chấm tĩnh, chấm động: 3.123453E12)
Ngoài ra Erlang còn cung cấp các cách viết khác:
• $char – trong đó char là các ký tự ASCII.
• Base#value – trong đó base là hệ cơ số của số được biểu diễn, value là cách ghi số
đã cho trong hệ cơ số base, trong đó base là các số từ 2 đến 36.
4.2.2.Atom
Trong Erlang chúng ta sử dụng các atom để định nghĩa các hằng số kí tự, khác với
trong C, để sử dụng các hằng số kí tự thì ta phải khai báo chúng ở phần đầu của chương
trình ví dụ như khai báo marco, hoặc khai báo trong một file header ví dụ như gobal.h, và
để sử dụng file này, ngay ở đầu chương trình chính ta phải có lệnh “#include gobal.h”.
Trong Erlang thì các hằng số kí tự là toàn cục, ta không cần phải sử dụng các định nghĩa
marco hoặc gọi các hàm include như trong C.
Đây là kiểu dữ liệu dạng chữ, dạng một hằng số tên. Như đã nói ở phần trước Atom
cần phải để trong dấu ngoặc đơn, nếu nó được bắt đầu bằng một chữ cái in hoa hoặc chứa
thêm các ký tự khác ngoài các chữ cái và số ra như dấu cách (space), dấu « _ » hoặc dấu
« @ ».
Ví dụ :
hello
phone_number
'Monday'
'phone number'
Nguyên lý các Ngôn ngữ lập trình
24
Lập trình song song và lập trình tương tranh, Tìm hiểu về ngôn ngữ lập trình
Erlang
Giá trị của atom chính là atom, có nghĩa là giá trị của một atom chính là hằng số kí
tự, vì erlang là một ngôn ngữ lập trình hàm nên mọi biến đều phải có giá trị, và giá trị đó
có thể là số hay atom (hằng số kí tự).
4.2.3.Fun, một kiểu dữ liệu khá đặc biệt.
Đây là một kiểu dữ liệu khá đặc biệt của các ngôn ngữ hỗ trợ lập trình hàm như các
ngôn ngữ Standard ML (SML) hay OCaml, … .Nếu như ở các ngôn ngữ không hỗ trợ lập
trình hàm thì kiểu dữ liệu mảng có thể được coi như một kiểu dữ liệu hàm nhưng khá hạn
chế và không hiệu quả.
Ngôn ngữ Erlang hỗ hợ kiểu dữ liệu hàm bằng khai báo như ví dụ sau:
1> Fun1 = fun (X) -> X+1 end.
#Fun<erl_eval.6.39074546>
2> Fun1(2).
3
Như trong đoạn code trên ta cũng thấy được việc tính giá trị của hàm trở nên rất đơn
giản chỉ cần viết TenHam(GiaTriCuaThamSo) là ta có thể tính được giá trị của hàm.
Tham số vào của một hàm, hay kết quả trả về của hàm có thể là một hàm khác. Nhờ
hỗ trợ như vậy nên ta có thể xây dựng các hàm cấp cao (high-order function), như ví dụ
dưới đây:
1> Double = fun(Fun, X) -> Fun(Fun(X)) end.
#Fun<erl_eval.12.113037538>
2> Nhanhai = fun(X) -> X*2 end.
#Fun<erl_eval.6.13229925>
3> Double(Nhanhai, 3).
12
Ta thấy hàm Double nhận đầu vào là một hàm với biến là X, kết quả trả ra là một
hàm thực hiện bằng cách thực hiện hai lần hàm đã cho.
4.2.4.Tuple và Record
Đây là kiểu dữ liệu tích Đề-Các của ngôn ngữ Erlang, là một tập hợp gồm các thành
phần dữ liệu có các kiểu khác nhau.
Nếu như trong C ta sử dụng cấu trúc struct để tạo nên kiểu dữ liệu có cấu trúc thể
hiện tích Đề Các của các kiểu dữ liệu đơn giản, bằng cách khai báo một struct như sau:
struct point {
Int x;
Int y;
} P;
Nguyên lý các Ngôn ngữ lập trình
25