2009-07-14 21 views
5

Khi các máy tính đa xử lý và đa lõi ngày càng trở nên phổ biến hơn, đơn giản là tắt một luồng mới một cách đơn giản và không đau để đơn giản hóa mã? Ví dụ, trong một dự án cá nhân hiện tại, tôi có một máy chủ mạng đang lắng nghe trên một cổng. Vì đây chỉ là một dự án cá nhân, nó chỉ là một ứng dụng máy tính để bàn, với một GUI tích hợp vào nó để cấu hình. Vì vậy, các ứng dụng đọc một cái gì đó như thế này:Việc kích hoạt một Chủ đề có phải là câu trả lời hợp lệ để đơn giản hóa mã không?

 
Main() 
    Read configuration 
    Start listener thread 
    Run GUI 

Listener Thread 
    While the app is running 
     Wait for a new connection 
     Run a client thread for the new connection 

Client Thread 
    Write synchronously 
    Read synchronously 
    ad inifinitum, or till they disconnect 

Cách tiếp cận này có nghĩa rằng trong khi tôi phải lo lắng về rất nhiều khóa, với các vấn đề tiềm năng có liên quan đến, tôi tránh rất nhiều mã spaghetti từ các cuộc gọi assynchronous vv

Một phiên bản hơi xảo quyệt của điều này đã xuất hiện hôm nay khi tôi đang làm việc trên mã khởi động. Việc khởi động nhanh chóng, nhưng nó sử dụng tải chậm cho rất nhiều cấu hình, điều đó có nghĩa là khi khởi động nhanh, thực sự kết nối và sử dụng dịch vụ rất khó khăn do độ trễ trong khi tải các phần khác nhau (điều này thực sự có thể đo lường được trong thực tế thời gian, lên đến 3-10 giây đôi khi). Vì vậy, tôi đã chuyển sang một chiến lược khác, khi khởi động, lặp lại tất cả mọi thứ và buộc tải chậm để khởi động ... nhưng điều này khiến nó bắt đầu chậm chạp; thức dậy, đi uống cà phê chậm. Giải pháp cuối cùng: ném vòng lặp vào một chuỗi riêng biệt với phản hồi trong khay hệ thống trong khi nó vẫn đang tải.

Đây có phải là "Meh, ném nó vào một chủ đề khác, nó sẽ ổn" thái độ ok? Tại thời điểm nào bạn bắt đầu nhận được lợi nhuận giảm và/hoặc thậm chí giảm hiệu suất?

+1

Câu hỏi hay và bổ sung tốt cho cơ sở kiến ​​thức SO. – Alan

+0

Tôi đồng ý, câu hỏi thực sự tốt. –

Trả lời

19

Đa luồng thực hiện rất nhiều việc, nhưng tôi không nghĩ rằng "đơn giản hóa" là một trong số đó.

+1

Nếu bạn phải thực hiện hai hoặc nhiều thứ đồng thời, thay thế duy nhất sẽ là vướng víu theo cách nào đó trong mã tuần tự. Vì vậy, theo nghĩa đó, đa luồng đơn giản hóa mã, mặc dù nó vẫn không đơn giản. – starblue

+1

Đó là đối số của tôi cho chuỗi + cuộc gọi đồng bộ cho ví dụ. triển khai giao thức mạng đồng bộ. Có, khóa và đồng bộ hóa không phải là chủ đề đơn giản ... Tôi nghĩ từ tôi đang tìm kiếm khi tôi viết câu hỏi này có lẽ là "củng cố" hoặc một cái gì đó tương tự. –

7

Đó là một cách tuyệt vời để giới thiệu các lỗi trong mã.

Sử dụng nhiều luồng đúng cách không dễ dàng. Nó không nên được cố gắng bởi các nhà phát triển mới.

+0

Tại sao lại là downvote. Bạn có cần đồng bộ hóa giải thích, bế tắc? –

+0

Để ghi lại, đó không phải là tôi. Và cá nhân, tôi không coi bản thân mình là một nhà phát triển mới, mặc dù đó có thể là niềm tự hào cá nhân nhảy vào. Tôi hiểu sự bế tắc và đồng bộ hóa, nếu không phải lúc nào các công cụ được sử dụng để đạt được/tránh chúng. –

+1

Tôi đã không nói về bạn như một nhà phát triển mới. Câu hỏi của bạn dường như là làm cho quy trình "thêm chủ đề mới" ở mọi nơi, điều đó có nghĩa là nó cũng sẽ được các nhà phát triển mới sử dụng. –

4

Theo tôi, lập trình đa luồng khá cao trên thang độ khó (và độ phức tạp), cùng với quản lý bộ nhớ. Với tôi, "Meh, ném nó vào một chủ đề khác, nó sẽ ổn" thái độ hơi quá bình thường. Hãy suy nghĩ lâu dài và khó khăn, bạn phải, trước khi giả mạo chủ đề bạn làm.

2

Điều này mang lại cho bạn thêm công việc gỡ lỗi điều kiện cuộc đua và xử lý các sự cố khóa và sycronisation.

Tôi sẽ không sử dụng điều này trừ khi có nhu cầu thực sự.

+0

Khóa nhiều hơn mức cần thiết, sau đó cắt chúng đi nơi chúng gây ra các vấn đề về hiệu năng sau khi xem xét kỹ lưỡng cách bạn có thể làm? –

0

Tôi nghĩ rằng bạn không có lựa chọn nào khác ngoài việc xử lý các chủ đề đặc biệt với kết nối mạng và kết nối đồng thời. Làm chủ đề làm cho mã đơn giản hơn? Tôi không nghĩ vậy. Nhưng nếu không có họ làm thế nào bạn sẽ lập trình một máy chủ có thể xử lý nhiều hơn 1 khách hàng cùng một lúc?

+0

Có, nhưng bạn không thêm chúng để làm cho mã đơn giản hơn. Bạn thêm chúng khi bạn _need_ chúng, và sau đó bạn làm như vậy một cách cẩn thận. –

+0

Ngoài ra, bằng cách sử dụng các cuộc gọi hoàn toàn không đồng bộ, bạn CÓ THỂ đạt được điều này, ít nhất là đến mức có liên quan đến mã của riêng bạn. Tất nhiên, các cuộc gọi này vẫn sử dụng các chủ đề riêng biệt đằng sau hậu trường ... –

+3

Và bạn vẫn gặp sự cố đồng bộ hóa. Ngoài ra, bạn sẽ ngạc nhiên nhưng rất nhiều người không thực sự nhận được mã async. –

4

No.

Đơn giản và đa luồng làm tăng độ phức tạp và là cách gần như nhỏ để thêm lỗi vào mã. Có concurrency vấn đề như synchronization, deadlock, race conditionspriority inversion để đặt tên một số.

Thứ hai, hiệu suất đạt được không tự động. Gần đây, có một xuất sắc article trong tạp chí MSDN dọc theo những dòng này. Các chi tiết nổi bật là một hoạt động nhất định đã lấy 46 giây cho mỗi mười lần lặp được mã hóa dưới dạng thao tác một luồng. Tác giả đã song song hoạt động một cách ngây thơ (một luồng cho mỗi bốn lõi) và hoạt động giảm xuống còn 30 giây cho mỗi 10 lần lặp.Âm thanh tuyệt vời cho đến khi bạn đi vào xem xét rằng các hoạt động hiện nay ăn thêm 300% sức mạnh xử lý nhưng chỉ có kinh nghiệm đạt được 34% hiệu quả. Nó không phải là giá trị tiêu thụ tất cả sức mạnh xử lý có sẵn cho một đạt được như thế.

+0

Tôi sẽ cung cấp cho bạn một +1 khác nếu tôi có thể cho liên kết bị đảo ngược mức độ ưu tiên ... không bao giờ nghe nói đến liên kết cụ thể đó trước đây. –

2

Đọc trên Amdahl's law, được tóm tắt tốt nhất bằng "Tốc độ của chương trình sử dụng nhiều bộ xử lý trong tính toán song song bị giới hạn bởi thời gian cần thiết cho phần tuần tự của chương trình".

Khi nó chỉ ra, nếu chỉ một phần nhỏ của ứng dụng của bạn có thể chạy song song bạn sẽ không nhận được nhiều lợi nhuận, nhưng có khả năng nhiều lỗi khó gỡ lỗi.

+0

Thông tin thú vị hơn bằng cách làm theo một vài liên kết: http://www.cilk.com/multicore-blog/bid/5365/What-the-is-Parallelism-Anyhow –

1

Tôi không có nghĩa là bị lật nhưng tệp cấu hình đó mất bao lâu để tải? Đó là nguồn gốc của vấn đề của bạn, phải không?

Trước khi sinh ra một sợi khác để xử lý nó, có lẽ nó có thể được parred xuống? Giảm, có lẽ đặt ở định dạng dữ liệu khác sẽ nhanh hơn, v.v ...?

Tần suất thay đổi? Có một cái gì đó bạn có thể phân tích một lần vào đầu ngày và đặt các biến trong bộ nhớ chia sẻ để chạy tiếp theo của chương trình chính của bạn chỉ có thể đính kèm và nhận được các giá trị cần thiết từ đó?

+0

Soạn thảo một loạt các tập lệnh C# ngắn. Có, nó có thể lưu trữ chúng giữa các lần chạy, và về lâu dài tôi có thể sẽ. Nhưng vì lợi ích của lập luận, và bộ não của tôi, tại thời điểm này tôi không. Hiện tại, họ không có bất kỳ dữ liệu duy nhất được bảo đảm nào được đính kèm với họ để kết nối chúng với phần còn lại của dữ liệu được liên kết với chúng. Đối với mức độ thường xuyên thay đổi, thường ít nhất một lần cho mỗi lần chạy. –

+0

Hai ý nghĩ sau đó. (1) Điều đó có lẽ đáng giá thêm độ phức tạp của các chủ đề bổ sung. (2) Bạn cũng có thể bắt đầu trên một số chương trình bộ nhớ đệm ngay bây giờ. Trong khi có thể có một số chồng chéo giữa các giải pháp ngắn hạn và dài hạn, bạn có thể sẽ làm hầu hết công việc hai lần. – Duck

+0

Đối với những gì nó có giá trị, tôi đã kết thúc quản lý để ThreadPool mỗi bộ sưu tập vì vậy bây giờ toàn bộ cấu hình được tải trong vài giây thay vì hơn một phút. Caching có thể sẽ vẫn còn trên chương trình nghị sự tại một số điểm ... nhưng nó không còn là một "cần phải làm" vấn đề của bất kỳ căng. –

1

Trong khi tôi đồng ý với mọi người khác ở đây nói rằng đa luồng không đơn giản hóa mã, nó có thể được sử dụng để đơn giản hóa trải nghiệm người dùng của ứng dụng của bạn.

Hãy xem xét một ứng dụng có nhiều tiện ích tương tác (tôi hiện đang phát triển một ứng dụng giúp ích) - trong quy trình làm việc của ứng dụng, người dùng có thể "xây dựng" dự án hiện tại đang làm việc. Điều này yêu cầu vô hiệu hóa các widget tương tác mà ứng dụng của tôi trình bày cho người dùng và trình bày một hộp thoại với thanh tiến trình không xác định và thông báo "vui lòng đợi" thân thiện.

"bản dựng" xuất hiện trên một chuỗi nền; nếu nó xảy ra trên giao diện người dùng, nó sẽ làm cho trải nghiệm người dùng kém thú vị - sau tất cả, không có gì thú vị khi bạn có thể nhấp vào tiện ích trong ứng dụng trong khi tác vụ nền đang chạy (ho, Visual Studio). Không phải để nói rằng VS không sử dụng chủ đề nền, tôi chỉ nói rằng trải nghiệm người dùng của họ có thể sử dụng một số cải tiến. Nhưng tôi lạc đề.

Điều tôi gặp vấn đề với tiêu đề bài đăng là bạn nghĩ đến việc tắt đề tài khi cần thực hiện tác vụ - tôi thường thích sử dụng lại các chủ đề - trong .NET, tôi thường ưu tiên sử dụng chuỗi hệ thống tạo ra một chủ đề mới mỗi khi tôi muốn làm điều gì đó, vì lợi ích của việc thực hiện.

+0

Sử dụng nhóm chủ đề, hoặc nếu không, bạn vẫn đang tạo một chuỗi mới, vì vậy tôi không thực sự thấy sự khác biệt, ngoài .NET đó là quản lý việc tạo và chạy nó, khi nào và cách nó phù hợp. –

+1

Không - cho mỗi MSDN: "Chỉ có một đối tượng ThreadPool trên mỗi tiến trình. Hồ bơi chủ đề được tạo lần đầu tiên bạn gọi ThreadPool.QueueUserWorkItem, hoặc khi một bộ đếm thời gian hoặc hoạt động chờ đăng ký được xếp hàng theo phương thức gọi lại" - chỉ có một thời gian trong việc tạo hồ bơi thread. Sau đó, nhóm các chủ đề được làm ấm và sống cho đến khi quá trình kết thúc - bạn chỉ cần gửi các công việc không đồng bộ đến nó và nó sẽ chăm sóc phần còn lại, trái với việc bạn tạo một chuỗi (nhiều chi phí) mỗi lần bạn muốn làm một cái gì đó. –

+0

Tôi nghĩ rằng anh ấy có nghĩa là tạo và chạy sợi chỉ, không phải của hồ bơi. –

1

Tôi sẽ cung cấp một số sự cân bằng chống lại "không" nhất trí.

KHUYẾN CÁO: Có, chủ đề phức tạp và có thể gây ra một loạt vấn đề. Mọi người khác đã chỉ ra điều này.

Từ kinh nghiệm, một chuỗi chặn lần đọc/ghi vào ổ cắm (yêu cầu một lỗ riêng biệt) là đơn giản hơn nhiều so với loại không chặn. Với các cuộc gọi chặn, bạn có thể cho biết trạng thái của kết nối chỉ bằng cách xem bạn đang ở đâu trong hàm. Với các cuộc gọi không chặn, bạn cần một loạt các biến để ghi lại trạng thái của kết nối và kiểm tra và sửa đổi chúng mỗi khi bạn tương tác với kết nối. Với các cuộc gọi chặn, bạn chỉ có thể nói "đọc các x byte tiếp theo" hoặc "đọc cho đến khi bạn tìm thấy X" và nó sẽ thực sự làm điều đó (hoặc không thành công). Với các cuộc gọi không chặn, bạn phải xử lý các dữ liệu bị phân mảnh mà thường yêu cầu giữ các bộ đệm tạm thời và điền chúng khi cần thiết. Bạn cũng sẽ kết thúc kiểm tra xem bạn đã nhận được đủ dữ liệu mỗi khi bạn nhận được ít hơn. Thêm vào đó bạn phải giữ một danh sách các kết nối mở và xử lý các đóng bất ngờ cho tất cả chúng.

Nó không nhận được đơn giản hơn nhiều so với điều này:

void WorkerThreadMain(Connection connection) { 
    Request request = ReadRequest(connection); 
    if(!request) return; 
    Reply reply = ProcessRequest(request); 
    if(!connection.isOpen) return; 
    SendReply(reply, connection); 
    connection.close(); 
} 

tôi muốn lưu ý rằng đây "nghe spawns tắt một sợi công nhân cho mỗi kết nối" mô hình là như thế nào các máy chủ web được thiết kế, và tôi giả đó là cách rất nhiều yêu cầu/đáp ứng mềm của các ứng dụng máy chủ được thiết kế.

Vì vậy, trong kết luận, tôi đã trải nghiệm mã spaghetti ổ cắm không đồng bộ mà bạn đã đề cập và sinh ra các chuỗi công nhân cho mọi kết nối đã trở thành một giải pháp tốt. Có nói tất cả điều này, ném chủ đề tại một vấn đề thường nên là phương sách cuối cùng của bạn.

Các vấn đề liên quan