Nhiều chủ đề có thể làm gì mà một chủ đề không thể? [đóng cửa]


100

Trong khi các luồng có thể tăng tốc độ thực thi mã, chúng có thực sự cần thiết không? Có thể thực hiện mọi đoạn mã bằng cách sử dụng một luồng hoặc có một thứ tồn tại chỉ có thể được thực hiện bằng cách sử dụng nhiều luồng không?


29
Dựa trên một số câu trả lời đã được đăng ở đây, tôi nghĩ rằng một số làm rõ là theo thứ tự. Mục đích của luồng là cho phép các máy tính (dường như) làm nhiều việc cùng một lúc. Nếu điều này được thực hiện bằng cách sử dụng một máy tính có lõi đơn, bạn sẽ không thấy bất kỳ sự tăng tốc tổng thể nào. Những gì bạn sẽ thấy là bỏ chặn; trong một chương trình đơn luồng, giao diện người dùng sẽ chặn trong khi các phép tính đang diễn ra. Trong một chương trình đa luồng, giao diện người dùng vẫn có thể sử dụng được trong khi các phép tính diễn ra trong nền.
Robert Harvey

11
Tất nhiên, nếu bạn có nhiều lõi xử lý trong máy tính (hiện nay là phổ biến), chương trình của bạn có thể sử dụng các luồng để tận dụng các lõi bổ sung để thực hiện quá trình xử lý của bạn và bạn sẽ thấy tăng tốc, vì bạn đang ném nhiều mã lực hơn vào vấn đề máy tính của bạn. Nếu bạn hoàn toàn không sử dụng các luồng, chương trình của bạn sẽ chỉ sử dụng một lõi xử lý duy nhất, sẽ ổn nếu đó là tất cả những gì bạn cần.
Robert Harvey

14
Một chương trình đơn luồng không thể khóa tài nguyên theo cách mà một chương trình đa luồng có thể.
zzzzBov

3
Trả lời: chạy trên hai lõi
Daniel Little

6
Trả lời: gây bế tắc.
Công việc

Câu trả lời:


111

Trước hết, các luồng không thể tăng tốc độ thực thi mã. Họ không làm cho máy tính chạy nhanh hơn. Tất cả những gì họ có thể làm là tăng hiệu quả của máy tính bằng cách sử dụng thời gian nếu không sẽ bị lãng phí. Trong một số loại xử lý, tối ưu hóa này có thể tăng hiệu quả và giảm thời gian chạy.

Câu trả lời đơn giản là đồng ý. Bạn có thể viết bất kỳ mã nào để chạy trên một luồng. Bằng chứng: Một hệ thống xử lý duy nhất chỉ có thể chạy hướng dẫn tuyến tính. Có nhiều dòng thực thi được thực hiện bởi các ngắt xử lý của hệ điều hành, lưu trạng thái của luồng hiện tại và bắt đầu một dòng khác.

Các phức tạp Câu trả lời là ... phức tạp hơn! Lý do mà các chương trình đa luồng thường có thể hiệu quả hơn các chương trình tuyến tính là do "vấn đề" phần cứng. CPU có thể thực hiện các phép tính nhanh hơn bộ nhớ và IO lưu trữ cứng. Vì vậy, một hướng dẫn "thêm", ví dụ, thực thi nhanh hơn nhiều so với "tìm nạp". Bộ nhớ cache và tìm nạp hướng dẫn chương trình chuyên dụng (không chắc chắn về thuật ngữ chính xác ở đây) có thể chống lại điều này ở một mức độ nào đó, nhưng vấn đề tốc độ vẫn còn.

Threading là một cách để chống lại sự không phù hợp này bằng cách sử dụng CPU cho các hướng dẫn ràng buộc CPU trong khi các lệnh IO đang hoàn tất. Một kế hoạch thực hiện luồng thông thường có thể sẽ là: Lấy dữ liệu, xử lý dữ liệu, ghi dữ liệu. Giả sử rằng việc tìm nạp và viết mất 3 chu kỳ và quá trình xử lý mất một, vì mục đích minh họa. Bạn có thể thấy rằng trong khi máy tính đang đọc hoặc viết, nó không làm trong 2 chu kỳ? Rõ ràng đó là lười biếng, và chúng ta cần phải phá roi tối ưu hóa của chúng tôi!

Chúng ta có thể viết lại quá trình sử dụng luồng để sử dụng thời gian lãng phí này:

  1. Tìm nạp số 1
  2. không hoạt động
  3. # 2 tìm nạp
  4. # 1 xong, xử lý nó
  5. viết số 1
  6. Tìm nạp số 1
  7. # 2 xong, xử lý nó
  8. viết số 2
  9. lấy số 2

Và như vậy. Rõ ràng đây là một ví dụ có phần khó hiểu, nhưng bạn có thể thấy kỹ thuật này có thể sử dụng thời gian mà nếu không thì sẽ chờ đợi IO.

Lưu ý rằng luồng như hình trên chỉ có thể tăng hiệu quả trên các quy trình bị ràng buộc nhiều IO. Nếu một chương trình chủ yếu là tính toán mọi thứ, sẽ không có nhiều "lỗ hổng" mà chúng ta có thể thực hiện nhiều công việc hơn. Ngoài ra, có một số hướng dẫn khi chuyển đổi giữa các luồng. Nếu bạn chạy quá nhiều luồng, CPU sẽ dành phần lớn thời gian để chuyển đổi và không thực sự giải quyết được vấn đề. Điều này được gọi là đập .

Đó là tất cả tốt và tốt cho một bộ xử lý lõi đơn, nhưng hầu hết các bộ xử lý hiện đại có hai lõi trở lên. Các luồng vẫn phục vụ cùng một mục đích - để tối đa hóa việc sử dụng CPU, nhưng lần này chúng tôi có khả năng chạy hai hướng dẫn riêng biệt cùng một lúc. Điều này có thể giảm thời gian chạy bởi một yếu tố tuy nhiên có nhiều lõi, bởi vì máy tính thực sự là đa nhiệm, không phải chuyển đổi ngữ cảnh.

Với nhiều lõi, các luồng cung cấp một phương pháp phân chia công việc giữa hai lõi. Những điều trên vẫn áp dụng cho từng lõi riêng lẻ; Một chương trình chạy hiệu suất tối đa với hai luồng trên một lõi rất có thể sẽ chạy ở hiệu suất cao nhất với khoảng bốn luồng trên hai lõi. (Hiệu quả được đo ở đây bằng cách thực hiện lệnh NOP tối thiểu.)

Các vấn đề với việc chạy các luồng trên nhiều lõi (trái ngược với một lõi) thường được xử lý bởi phần cứng. CPU sẽ chắc chắn rằng nó khóa các vị trí bộ nhớ phù hợp trước khi đọc / ghi vào nó. (Tôi đã đọc rằng nó sử dụng một bit cờ đặc biệt trong bộ nhớ cho việc này, nhưng điều này có thể được thực hiện theo nhiều cách.) Là một lập trình viên với các ngôn ngữ cấp cao hơn, bạn không phải lo lắng về bất cứ điều gì nhiều hơn trên hai lõi như bạn sẽ phải với một.

TL; DR: Chủ đề có thể phân chia công việc để cho phép máy tính xử lý một số tác vụ không đồng bộ. Điều này cho phép máy tính chạy với hiệu suất tối đa bằng cách sử dụng tất cả thời gian xử lý có sẵn, thay vì khóa khi một quy trình đang chờ tài nguyên.


17
Câu trả lời tuyệt vời. Tôi chỉ muốn giải thích rằng sau một thời điểm nhất định, việc thêm nhiều luồng thực sự có thể khiến chương trình chạy chậm hơn , bởi vì tất cả thời gian "lãng phí" trước đây được sử dụng và giờ bạn chỉ thêm thêm chuyển đổi ngữ cảnh và phí đồng bộ hóa.
Karl Bielefeldt

8
+1 để mô tả cách các chủ đề trợ giúp xử lý chặn IO. Một cách sử dụng chủ đề khác là cho phép UI tiếp tục xử lý các hành động của người dùng (di chuyển chuột, thanh tiến trình, tổ hợp phím) để nhận thức của người dùng là chương trình chưa bị 'đóng băng'.
jqa

3
Thêm một điều nữa: nói đúng ra , các chủ đề không thêm bất kỳ biểu cảm nào vào hầu hết các ngôn ngữ; bạn hoàn toàn có thể, nếu bạn hoàn toàn phải tự thực hiện tất cả việc ghép kênh và nhận được kết quả tương tự. Tuy nhiên, trên thực tế, điều này sẽ giúp thực hiện lại toàn bộ thư viện luồng của bạn - giống như việc đệ quy không thực sự cần thiết, nhưng có thể được mô phỏng bằng cách viết ngăn xếp cuộc gọi của riêng bạn.
Kilian Foth

9
@james: Tôi sẽ phân loại xử lý UI như một dạng I / O khác. Có lẽ điều tồi tệ nhất, khi người dùng có xu hướng bị lag và phi tuyến tính. :)
TMN

7
Có một giả định im lặng trong câu hỏi và câu trả lời xung quanh máy tính là lõi đơn hoặc CPU. Đồng hồ của tôi có hai lõi, tôi nghi ngờ giả định này là hợp lệ trên bất kỳ máy móc hiện đại nào.
blueberryfields

37

Nhiều chủ đề có thể làm gì mà một chủ đề không thể?

Không có gì.

Phác thảo bằng chứng đơn giản:

  • [Phỏng đoán Church-Turing] ⇒ Mọi thứ có thể được tính toán đều có thể được tính bằng Máy Turing phổ dụng.
  • Một máy Turing phổ dụng là một luồng đơn.
  • Ergo, mọi thứ có thể được tính toán có thể được tính bằng một luồng duy nhất.

Tuy nhiên, lưu ý rằng có một giả định lớn ẩn trong đó: cụ thể là ngôn ngữ được sử dụng trong một luồng duy nhất là Turing-Complete.

Vì vậy, câu hỏi thú vị hơn sẽ là: "Có thể chỉ thêm đa luồng vào một ngôn ngữ không hoàn chỉnh Turing khiến nó trở nên hoàn chỉnh?" Và tôi tin rằng, câu trả lời là "Có".

Hãy lấy Tổng số ngôn ngữ chức năng. [Đối với những người không quen thuộc: giống như Lập trình chức năng là Lập trình với Hàm, Lập trình tổng số là Lập trình với Tổng số chức năng.]

Tổng số ngôn ngữ chức năng rõ ràng là không hoàn chỉnh Turing: bạn không thể viết một vòng lặp vô hạn trong TFPL (thực tế, đó là định nghĩa của "tổng số"), nhưng bạn có thể trong Turing Machine, ergo tồn tại ít nhất một chương trình không thể được viết bằng TFPL nhưng có thể bằng UTM, do đó TFPL ít mạnh hơn về mặt tính toán so với UTM.

Tuy nhiên, ngay khi bạn thêm luồng vào TFPL, bạn sẽ nhận được các vòng lặp vô hạn: chỉ cần thực hiện mỗi lần lặp của vòng lặp trong một luồng mới. Mỗi luồng riêng lẻ luôn trả về một kết quả, do đó là Total, nhưng mỗi luồng cũng sinh ra một luồng mới thực hiện lần lặp tiếp theo , ad infinitum.

Tôi nghĩ rằng ngôn ngữ này sẽ là Turing-Complete.

Ít nhất, nó trả lời câu hỏi gốc:

Nhiều chủ đề có thể làm gì mà một chủ đề không thể?

Nếu bạn có một ngôn ngữ không thể thực hiện các vòng lặp vô hạn, thì đa luồng cho phép bạn thực hiện các vòng lặp vô hạn.

Tất nhiên, lưu ý rằng việc sinh ra một luồng là một tác dụng phụ và do đó, ngôn ngữ mở rộng của chúng tôi không chỉ không còn là Tổng, nó thậm chí không còn là Chức năng nữa.


13
Vì bạn đã đưa ra một "bằng chứng", tôi không thể ngăn bản thân mình "tốt, thực sự ..." trong bạn. Tôi nghĩ rằng bạn đang lạm dụng khái niệm tính toán. Bằng chứng là một điều dễ thương và tôi hiểu ý của bạn, nhưng nó không thực sự ở mức độ trừu tượng đúng. Không thể làm mọi thứ không giống như không thể tính toán , ví dụ, bạn không thể sử dụng bằng chứng của mình để nói, bởi vì máy Turing có thể tính toán "mọi chức năng tính toán", nó có thể thực hiện trong 3 giây. Nói một cách chính xác, một máy có luồng đơn không bị gián đoạn không thể truy cập I / O trong khi vẫn giữ cho bộ xử lý của nó bận thực hiện tính toán.
xmm0

1
Và, không có máy tính thực sự là một máy Turing.
Jens

1
@ColeJohnson: Bế tắc là một chi tiết thực hiện. Đầu ra có thể nhìn thấy là "không dừng lại", điều này có thể dễ dàng thực hiện được với một luồng.
Heinzi

1
@Heinzi: "dễ dàng thực hiện được" là một cách nói nhẹ nhàng. Nếu tôi nhớ chính xác, chương trình thứ hai hoặc thứ ba mà tôi từng viết đã làm chính xác điều đó! ;-)
Joachim Sauer

1
Nếu vũ trụ là chủ đề đơn ed, thì lý thuyết dây là gì? Bạn không thể tranh luận với khoa học như thế này.
corsiKa

22

Về lý thuyết, mọi thứ mà một chương trình đa luồng cũng có thể được thực hiện với một chương trình đơn luồng, chỉ chậm hơn.

Trong thực tế, sự khác biệt về tốc độ có thể rất lớn, không có cách nào người ta có thể sử dụng một chương trình đơn luồng cho nhiệm vụ. Ví dụ: nếu bạn có một công việc xử lý dữ liệu hàng loạt chạy mỗi đêm và phải mất hơn 24 giờ để hoàn thành một luồng, bạn không có lựa chọn nào khác ngoài việc làm cho nó đa luồng. (Trong thực tế, ngưỡng có thể thậm chí còn ít hơn: thường các tác vụ cập nhật như vậy phải kết thúc vào sáng sớm, trước khi người dùng bắt đầu sử dụng lại hệ thống. Ngoài ra, các tác vụ khác có thể phụ thuộc vào chúng, cũng phải hoàn thành trong cùng một đêm. thời gian chạy có sẵn có thể thấp đến vài giờ / phút.)

Làm công việc tính toán trên nhiều luồng là một hình thức xử lý phân tán; bạn đang phân phối công việc qua nhiều chủ đề. Một ví dụ khác về xử lý phân tán (sử dụng nhiều máy tính thay vì nhiều luồng) là trình bảo vệ màn hình SETI: việc xử lý nhiều dữ liệu đo trên một bộ xử lý sẽ mất nhiều thời gian và các nhà nghiên cứu muốn xem kết quả trước khi nghỉ hưu ;-) Tuy nhiên, họ không có ngân sách để thuê một siêu máy tính quá lâu, vì vậy họ phân phối công việc trên hàng triệu máy tính cá nhân để làm cho nó rẻ.


Trình bảo vệ màn hình SETI là một ví dụ về luồng theo nghĩa là nó chạy trên một luồng nền trong máy tính của bạn; bạn vẫn có thể làm những việc khác trên máy tính của mình trong khi nó đang xử lý dữ liệu ở chế độ nền. Nếu không có luồng, sẽ có một khoảng dừng trong khi trình bảo vệ màn hình SETI thực hiện các tính toán của nó; bạn sẽ phải đợi nó kết thúc trước khi bạn có thể tiếp tục công việc của chính mình.
Robert Harvey

3
@Robert: Tôi nghĩ Péter đang sử dụng SETI @ Home để giải thích khái niệm chia nhỏ công việc để chạy trên nhiều bộ xử lý. Điện toán phân tán tất nhiên khác với đa luồng, nhưng khái niệm này tương tự nhau.
Steven

5
Tôi nghĩ rằng ví dụ SETI là tính toán phân tán chứ không phải là luồng. Khái niệm tương tự nhưng không giống nhau.

11

Mặc dù các chủ đề dường như là một bước nhỏ từ tính toán tuần tự, trên thực tế, chúng đại diện cho một bước rất lớn. Họ loại bỏ các tính chất cần thiết và hấp dẫn nhất của tính toán tuần tự: dễ hiểu, dự đoán và xác định. Các chủ đề, như là một mô hình tính toán, cực kỳ không phổ biến, và công việc của lập trình viên trở thành một trong những cách cắt tỉa không thuyết phục đó.

- Vấn đề với chủ đề (www.eecs.ber siêu.edu / Pub / TechRpts / 2006 / EECS-2006-1.pdf).

Mặc dù có một số lợi thế về hiệu suất có thể có bằng cách sử dụng các luồng trong đó bạn có thể phân phối công việc trên nhiều lõi, chúng thường có giá rất cao.

Một trong những nhược điểm của việc sử dụng các luồng chưa được đề cập ở đây là việc mất khả năng phân chia tài nguyên mà bạn nhận được với các không gian xử lý luồng đơn. Ví dụ, giả sử bạn gặp phải trường hợp segfault. Trong một số trường hợp, có thể phục hồi từ điều này trong một ứng dụng đa quy trình ở chỗ bạn chỉ cần để đứa trẻ bị lỗi chết đi và hồi sinh một cái mới. Đây là trường hợp trong phần phụ trợ prefork của Apache. Khi một cá thể httpd gặp khó khăn, trường hợp xấu hơn là yêu cầu HTTP cụ thể có thể bị loại bỏ cho quá trình đó nhưng Apache sinh ra một đứa trẻ mới và thường là yêu cầu nếu chỉ bực bội và được phục vụ. Kết quả cuối cùng là toàn bộ Apache không bị gỡ xuống với luồng bị lỗi.

Một xem xét khác trong kịch bản này là rò rỉ bộ nhớ. Có một số trường hợp bạn có thể xử lý một sự cố một cách duyên dáng (trên UNIX, phục hồi từ một số tín hiệu cụ thể - thậm chí segfault / fpviolation - có thể), nhưng ngay cả trong trường hợp đó, bạn có thể đã rò rỉ tất cả bộ nhớ được phân bổ bởi luồng đó (malloc, mới, v.v.). Vì vậy, trong khi bạn xử lý có thể tồn tại, nó sẽ rò rỉ bộ nhớ ngày càng nhiều theo thời gian với mỗi lỗi / phục hồi. Một lần nữa, có một số cách để giảm thiểu điều này như việc sử dụng nhóm bộ nhớ của Apache. Nhưng điều này vẫn không bảo vệ chống lại bộ nhớ có thể được phân bổ bởi các lib của bên thứ ba mà luồng có thể đang sử dụng.

Và, như một số người đã chỉ ra, hiểu được nguyên thủy đồng bộ hóa có lẽ là điều khó nhất để thực sự đúng. Vấn đề này tự nó - chỉ cần có logic chung cho tất cả mã của bạn - có thể là một vấn đề đau đầu. Những bế tắc bí ẩn có xu hướng xảy ra vào những thời điểm kỳ lạ nhất, và đôi khi thậm chí là cho đến khi chương trình của bạn đang chạy trong sản xuất, điều này khiến việc gỡ lỗi trở nên khó khăn hơn. Thêm vào đó là thực tế rằng các nguyên thủy đồng bộ hóa thường khác nhau nhiều so với nền tảng (Windows so với POSIX) và việc gỡ lỗi thường có thể khó khăn hơn, cũng như khả năng cho các điều kiện cuộc đua bất cứ lúc nào (khởi động / khởi tạo, thời gian chạy và tắt máy), lập trình với các chủ đề thực sự có chút thương xót cho người mới bắt đầu. Và ngay cả đối với các chuyên gia, Vẫn còn chút thương xót chỉ vì kiến ​​thức về luồng tự nó không giảm thiểu sự phức tạp nói chung. Mỗi dòng mã đôi khi dường như gộp theo cấp số nhân độ phức tạp chung của chương trình cũng như tăng xác suất cho một bế tắc ẩn hoặc điều kiện cuộc đua kỳ lạ xuất hiện bất cứ lúc nào. Cũng có thể rất khó để viết các trường hợp thử nghiệm để loại bỏ những thứ này ra.

Đây là lý do tại sao một số dự án như Apache và PostgreSQL dành cho hầu hết các phần dựa trên quy trình. PostgreSQL chạy mọi luồng phụ trợ trong một quy trình riêng biệt. Tất nhiên điều này vẫn không làm giảm bớt vấn đề đồng bộ hóa và điều kiện chủng tộc, nhưng nó có thêm một chút bảo vệ và trong một số cách đơn giản hóa mọi thứ.

Nhiều tiến trình, mỗi tiến trình chạy một luồng thực thi có thể tốt hơn nhiều luồng chạy trong một tiến trình. Và với sự ra đời của nhiều mã ngang hàng mới như AMQP (RabbitMQ, Qpid, v.v.) và ZeroMQ, việc phân chia các luồng trên các không gian xử lý khác nhau và thậm chí cả máy móc và mạng, đơn giản hóa rất nhiều thứ. Nhưng vẫn không phải là viên đạn bạc. Vẫn còn phức tạp để giải quyết. Bạn chỉ cần di chuyển một số biến của mình từ không gian xử lý vào mạng.

Điểm mấu chốt là quyết định nhập vào miền của các luồng không phải là một quyết định nhẹ. Một khi bạn bước vào lãnh thổ đó, gần như ngay lập tức mọi thứ trở nên phức tạp hơn và những vấn đề hoàn toàn mới xâm nhập vào cuộc sống của bạn. Nó có thể vui vẻ và tuyệt vời, nhưng nó giống như năng lượng hạt nhân - khi mọi thứ trở nên tồi tệ, chúng có thể trở nên tồi tệ và nhanh chóng. Tôi nhớ đã tham gia một lớp đào tạo về phê bình nhiều năm trước và họ đã cho thấy hình ảnh của một số nhà khoa học tại Los Alamos, người đã chơi với plutonium trong các phòng thí nghiệm trong Thế chiến II. Nhiều người đã thực hiện rất ít hoặc không có biện pháp phòng ngừa đối với sự kiện phơi nhiễm và trong chớp mắt - trong một chớp sáng, không đau, tất cả sẽ kết thúc với họ. Mấy ngày sau họ chết. Richard Feynman sau này gọi điều này là " cù đuôi rồng"Đó là kiểu chơi với các chủ đề có thể giống như (ít nhất là đối với tôi). Lúc đầu, nó có vẻ khá vô hại, và khi bạn bị cắn, bạn gãi đầu về việc mọi thứ trở nên chua chát. Nhưng ít nhất thì các chủ đề đã thắng Sẽ không giết bạn.


TL của tôi trả lời nhạt so với tác phẩm hay này về những chủ đề duy nhất sẽ không làm cho bạn.
bmike

1
nghỉ giải lao buổi chiều
Mike Owens

10

Đầu tiên, một ứng dụng luồng đơn sẽ không bao giờ tận dụng CPU đa lõi hoặc siêu phân luồng. Nhưng ngay cả trên một lõi đơn, CPU đơn luồng thực hiện đa luồng có lợi thế.

Hãy xem xét sự thay thế và liệu điều đó có làm bạn hạnh phúc không. Giả sử bạn có nhiều tác vụ cần chạy đồng thời. Chẳng hạn, bạn phải tiếp tục liên lạc với hai hệ thống khác nhau. Làm thế nào để bạn làm điều này mà không đa luồng? Bạn có thể sẽ tạo lịch trình của riêng bạn và để nó gọi các nhiệm vụ khác nhau cần được thực hiện. Điều này có nghĩa là bạn cần chia các nhiệm vụ của mình thành nhiều phần. Bạn có thể cần phải đáp ứng một số hạn chế trong thời gian thực, bạn phải đảm bảo rằng các bộ phận của bạn không chiếm quá nhiều thời gian. Nếu không, bộ đếm thời gian sẽ hết hạn trong các nhiệm vụ khác. Điều này làm cho việc phân chia một nhiệm vụ trở nên khó khăn hơn. Càng nhiều nhiệm vụ bạn cần để quản lý bản thân, bạn càng cần phải chia nhỏ và lịch trình của bạn sẽ trở nên phức tạp hơn để đáp ứng tất cả các ràng buộc.

Khi bạn có nhiều chủ đề cuộc sống có thể trở nên dễ dàng hơn. Một bộ lập lịch ưu tiên có thể dừng một luồng bất cứ lúc nào, giữ trạng thái của nó và tái (bắt đầu) một luồng khác. Nó sẽ khởi động lại khi chủ đề của bạn đến lượt của nó. Ưu điểm: sự phức tạp của việc viết lịch trình đã được thực hiện cho bạn và bạn không phải chia nhỏ các nhiệm vụ của mình. Ngoài ra, bộ lập lịch có khả năng quản lý các quy trình / chủ đề mà chính bạn thậm chí không biết. Và ngoài ra, khi một luồng không cần làm gì cả (nó đang chờ một sự kiện nào đó) thì nó sẽ không mất chu kỳ CPU. Điều này không dễ thực hiện khi bạn đang tạo bộ lập lịch đơn luồng. (đặt một cái gì đó vào giấc ngủ không quá khó, nhưng làm thế nào để nó thức dậy?)

Nhược điểm của phát triển đa luồng là bạn cần hiểu về các vấn đề tương tranh, khóa chiến lược, v.v. Phát triển mã đa luồng không có lỗi có thể khá khó khăn. Và gỡ lỗi có thể còn khó hơn.


9

Có một cái gì đó tồn tại mà chỉ có thể được thực hiện bằng cách sử dụng nhiều luồng?

Đúng. Bạn không thể chạy mã trên nhiều CPU hoặc lõi CPU bằng một luồng.

Không có nhiều CPU / lõi, các luồng vẫn có thể đơn giản hóa mã chạy theo khái niệm song song, chẳng hạn như xử lý máy khách trên máy chủ - nhưng bạn có thể làm điều tương tự mà không cần luồng.


6

Chủ đề không chỉ về tốc độ mà còn về sự tương tranh.

Nếu bạn chưa có ứng dụng hàng loạt như @Peter đề xuất mà thay vào đó là bộ công cụ GUI như WPF, làm thế nào bạn có thể tương tác với người dùng và logic kinh doanh chỉ bằng một luồng?

Ngoài ra, giả sử bạn đang xây dựng một Máy chủ Web. Làm thế nào bạn sẽ phục vụ đồng thời nhiều hơn một người dùng chỉ với một luồng (giả sử không có quy trình nào khác)?

Có nhiều kịch bản chỉ cần một chủ đề đơn giản là không đủ. Đó là lý do tại sao những tiến bộ gần đây như bộ xử lý Intel MIC với hơn 50 lõi và hàng trăm luồng đang diễn ra.

Có, lập trình song song và đồng thời là khó. Nhưng cần thiết.


3
"Chỉ một luồng" có thể có nghĩa là một số điều - chẳng hạn, bạn có thể sử dụng các phần tiếp theo được phân tách để mô phỏng đa nhiệm hợp tác trong một luồng (OS hoặc green), chẳng hạn.
Frank Shearar

Chắc chắn, nhưng tôi đang đưa ra một bối cảnh nhỏ cho câu hỏi @Frank +1 cho tiền boa.
Randolf Rincón Fadul

3
Chủ đề có thể làm cho nó dễ dàng hơn, nhưng tất cả các nhiệm vụ mà bạn đề cập có thể được thực hiện mà không có chủ đề, và thực sự được sử dụng. Chẳng hạn, vòng lặp GUI chuẩn được sử dụng là while (true) {xử lý các sự kiện GUI; tính toán một chút}. Không có chủ đề cần thiết.
KeithB

Có các máy chủ web đơn luồng xử lý đơn. chẳng hạn như lighttpd, hoặc thttpd hoặc nginx (mặc dù sau này có thể chạy nhiều hơn một tiến trình, nhưng mặc định là một. Nó cũng luôn luôn là một luồng). Họ hoàn toàn không có vấn đề gì khi phục vụ nhiều hơn một người dùng.
StasM

6

Đa luồng có thể cho phép giao diện GUI vẫn phản hồi trong các hoạt động xử lý dài. Nếu không có đa luồng, người dùng sẽ bị kẹt khi xem một biểu mẫu bị khóa trong khi một quá trình dài đang chạy.


Điều đó không hoàn toàn chính xác. Về lý thuyết, bạn có thể chia hoạt động dài của mình thành nhiều hoạt động nhỏ hơn và cập nhật xử lý sự kiện ở giữa.
Sebastian Negraszus

@Sebastion N. Nhưng nếu quá trình dài không thể được tái cấu trúc thành những cái nhỏ hơn? Khả năng chạy tiến trình trong một luồng khác có thể giải phóng luồng GUI (luồng chính) để vẫn đáp ứng.
LarsTech

5

Mã đa luồng có thể bế tắc logic chương trình và truy cập dữ liệu cũ theo cách mà các luồng đơn không thể.

Các chủ đề có thể nhận được một lỗi tối nghĩa từ một thứ mà một lập trình viên trung bình có thể dự kiến ​​sẽ gỡ lỗi và chuyển nó vào vương quốc nơi những câu chuyện được kể về sự may mắn cần thiết để bắt được lỗi tương tự với nó khi một lập trình viên cảnh báo tình cờ nhìn vào thời điểm thích hợp.


4

các ứng dụng xử lý chặn IO cũng cần duy trì phản hồi với các đầu vào khác (GUI hoặc các kết nối khác) không thể được thực hiện đơn lẻ

việc bổ sung các phương thức kiểm tra trong IO lib để xem có thể đọc được bao nhiêu mà không chặn có thể giúp điều này nhưng không có nhiều thư viện đảm bảo đầy đủ về điều này


Chúng có thể là một luồng đơn (và thường là). Mã này bảo kernel khởi động một số IO và nhận được tín hiệu khi hoàn thành. Trong khi chờ tín hiệu, nó có thể xử lý khác.
KeithB

@keith đó là không chặn IO Tôi đặc biệt nói rằng chặn
ratchet freak

Nhưng tại sao bạn lại chặn IO nếu bạn muốn duy trì phản hồi? Có bất kỳ IO chặn nào thường không có giải pháp thay thế không chặn không? Tôi không thể nghĩ về bất kỳ.
KeithB

@keith RMI của java đang chặn và trong khi bạn có thể thực hiện một cuộc gọi lại (nhưng điều đó tạo ra một luồng khác) với nó nhưng nó có thể bị chặn bởi tường lửa
ratchet freak

Nghe có vẻ như là một giới hạn của Java RMI, không phải là giới hạn vốn có của mã đơn luồng. Ngoài ra, nếu bạn đang thực hiện RMI, bạn có một "luồng" thực thi riêng, chỉ trên một máy từ xa.
KeithB

4

Rất nhiều câu trả lời hay nhưng tôi không chắc có bất kỳ cụm từ nào hoàn toàn như tôi muốn - Có lẽ điều này cung cấp một cách khác để xem xét nó:

Các chủ đề chỉ là một đơn giản hóa lập trình như Đối tượng hoặc Diễn viên hoặc cho các vòng lặp (Có, bất cứ điều gì bạn thực hiện với các vòng lặp bạn có thể thực hiện với if / goto).

Không có chủ đề, bạn chỉ cần thực hiện một công cụ nhà nước. Tôi đã phải làm điều này nhiều lần (Lần đầu tiên tôi làm điều đó, tôi chưa bao giờ nghe về nó - chỉ đưa ra một tuyên bố chuyển đổi lớn được kiểm soát bởi một biến "Trạng thái"). Máy trạng thái vẫn còn khá phổ biến nhưng có thể gây phiền nhiễu. Với các chủ đề, một khối lớn của nồi hơi biến mất.

Chúng cũng tình cờ giúp ngôn ngữ dễ dàng hơn trong việc phá vỡ sự thực thi thời gian chạy của nó thành các khối thân thiện với nhiều CPU (Tôi cũng vậy, các diễn viên cũng vậy).

Java cung cấp các luồng "Xanh" trên các hệ thống mà HĐH không cung cấp bất kỳ hỗ trợ luồng nào. Trong trường hợp này, dễ dàng nhận thấy rằng chúng rõ ràng không có gì khác hơn là một sự trừu tượng hóa lập trình.


+1 - Chủ đề chỉ cung cấp một sự trừu tượng hóa cho sự song song trên các máy cơ bản tuần tự, giống như các ngôn ngữ lập trình cấp cao cung cấp sự trừu tượng hóa mã máy.
mouviciel

0

Các hệ điều hành sử dụng khái niệm cắt thời gian trong đó mỗi luồng sẽ có thời gian để chạy và sau đó được ưu tiên. Cách tiếp cận như thế có thể thay thế luồng như hiện tại, nhưng viết lịch trình của riêng bạn trong mọi ứng dụng sẽ là quá mức cần thiết. Hơn nữa, bạn phải làm việc với các thiết bị I / O, v.v. Và sẽ yêu cầu một số hỗ trợ từ phía phần cứng, để bạn có thể kích hoạt các ngắt để chạy lịch trình của bạn. Về cơ bản, bạn sẽ viết một hệ điều hành mới mỗi lần.

Trong phân luồng chung có thể cải thiện hiệu suất trong trường hợp các luồng chờ I / O hoặc đang ngủ. Nó cũng cho phép bạn tạo các giao diện phản hồi nhanh và cho phép dừng các quá trình trong khi bạn thực hiện các tác vụ dài. Ngoài ra, phân luồng cải thiện mọi thứ trên CPU đa lõi thực sự.


0

Đầu tiên, các luồng có thể làm hai hoặc nhiều việc cùng một lúc (nếu bạn có nhiều hơn một lõi). Mặc dù bạn cũng có thể thực hiện việc này với nhiều quy trình, một số tác vụ chỉ không phân phối trên nhiều quy trình rất tốt.

Ngoài ra, một số tác vụ có khoảng trắng trong đó mà bạn không thể dễ dàng tránh được. Ví dụ, thật khó để đọc dữ liệu từ một tệp trên đĩa và đồng thời, quá trình của bạn sẽ làm một cái gì đó khác cùng một lúc. Nếu nhiệm vụ của bạn nhất thiết đòi hỏi nhiều dữ liệu đọc từ đĩa, quá trình của bạn sẽ mất rất nhiều thời gian chờ đợi cho dù bạn làm gì.

Thứ hai, các luồng có thể cho phép bạn tránh phải tối ưu hóa số lượng lớn mã của bạn không quan trọng về hiệu năng. Nếu bạn chỉ có một luồng duy nhất, mỗi đoạn mã là hiệu suất quan trọng. Nếu nó bị chặn, bạn bị chìm - không có nhiệm vụ nào được thực hiện bởi quá trình đó có thể tiến triển. Với các luồng, một khối sẽ chỉ ảnh hưởng đến luồng đó và các luồng khác có thể xuất hiện và hoạt động trên các tác vụ cần được thực hiện bởi quy trình đó.

Một ví dụ điển hình là mã xử lý lỗi được thực thi không thường xuyên. Giả sử một tác vụ gặp phải một lỗi rất không thường xuyên và mã để xử lý lỗi đó cần phải chuyển vào bộ nhớ. Nếu đĩa đang bận và quá trình chỉ có một luồng duy nhất, không có tiến trình chuyển tiếp nào có thể được thực hiện cho đến khi mã để xử lý lỗi đó có thể được tải vào bộ nhớ. Điều này có thể gây ra phản ứng bùng nổ.

Một ví dụ khác là nếu bạn rất hiếm khi phải thực hiện tra cứu cơ sở dữ liệu. Nếu bạn chờ cơ sở dữ liệu trả lời, mã của bạn sẽ gặp một độ trễ rất lớn. Nhưng bạn không muốn gặp rắc rối khi làm cho tất cả các mã này không đồng bộ bởi vì rất hiếm khi bạn cần thực hiện các tra cứu này. Với một chủ đề để làm công việc này, bạn có được tốt nhất của cả hai thế giới. Một chủ đề để làm công việc này làm cho nó không hiệu suất quan trọng như nó phải được.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.