Sử dụng ThreadPool.QueueUserWorkItem trong ASP.NET trong trường hợp lưu lượng truy cập cao


111

Tôi luôn có ấn tượng rằng việc sử dụng ThreadPool cho các tác vụ nền trong thời gian ngắn (giả sử không quan trọng) được coi là phương pháp hay nhất, ngay cả trong ASP.NET, nhưng sau đó tôi xem qua bài viết này có vẻ gợi ý khác - lập luận là bạn nên rời khỏi ThreadPool để xử lý các yêu cầu liên quan đến ASP.NET.

Vì vậy, đây là cách tôi đã thực hiện các tác vụ không đồng bộ nhỏ cho đến nay:

ThreadPool.QueueUserWorkItem(s => PostLog(logEvent))

Và thay vào đó, bài viết đề xuất tạo một chuỗi rõ ràng, tương tự như:

new Thread(() => PostLog(logEvent)){ IsBackground = true }.Start()

Phương pháp đầu tiên có lợi thế là được quản lý và bị ràng buộc, nhưng có khả năng xảy ra (nếu bài viết là chính xác) rằng các tác vụ nền sau đó đang cạnh tranh cho các luồng với trình xử lý yêu cầu ASP.NET. Phương pháp thứ hai giải phóng ThreadPool, nhưng với chi phí là không bị ràng buộc và do đó có khả năng sử dụng quá nhiều tài nguyên.

Vậy câu hỏi của tôi là lời khuyên trong bài viết có đúng không?

Nếu trang web của bạn nhận được nhiều lưu lượng truy cập đến mức ThreadPool của bạn đang đầy, thì tốt hơn là bạn nên vượt ra ngoài phạm vi hoặc một ThreadPool đầy đủ ngụ ý rằng bạn đang đạt đến giới hạn tài nguyên của mình, trong trường hợp đó bạn không nên cố gắng bắt đầu chủ đề của riêng bạn?

Làm rõ: Tôi chỉ hỏi trong phạm vi các tác vụ không đồng bộ nhỏ không quan trọng (ví dụ: ghi nhật ký từ xa), không phải các hạng mục công việc đắt tiền sẽ yêu cầu một quy trình riêng (trong những trường hợp này, tôi đồng ý rằng bạn sẽ cần một giải pháp mạnh mẽ hơn).


Cốt truyện dày lên - tôi tìm thấy bài viết này ( blog.msdn.com/nicd/archive/2007/04/16/… ), mà tôi không thể giải mã được. Một mặt, có vẻ như đang nói rằng IIS 6.0+ luôn xử lý các yêu cầu trên các luồng nhân viên nhóm luồng (và các phiên bản trước đó có thể làm như vậy), nhưng sau đó có điều này: "Tuy nhiên, nếu bạn đang sử dụng các trang không đồng bộ .NET 2.0 mới ( Async = "true") hoặc ThreadPool.QueueUserWorkItem (), thì phần xử lý không đồng bộ sẽ được thực hiện bên trong [một luồng cổng hoàn thành]. " Phần không đồng bộ của quá trình xử lý ?
Jeff Sternal

Một điều khác - điều này sẽ đủ dễ dàng để kiểm tra trên bản cài đặt IIS 6.0+ (mà tôi không có ngay bây giờ) bằng cách kiểm tra xem các luồng công nhân có sẵn của nhóm luồng có thấp hơn luồng công nhân tối đa của nó hay không, sau đó thực hiện tương tự trong hàng đợi hạng mục công việc.
Jeff Sternal

Câu trả lời:


104

Các câu trả lời khác ở đây dường như đang bỏ sót điểm quan trọng nhất:

Trừ khi bạn đang cố gắng song song một hoạt động đòi hỏi nhiều CPU để hoàn thành nó nhanh hơn trên một trang web tải thấp, nếu không thì chẳng ích gì khi sử dụng một chuỗi công nhân cả.

Điều đó áp dụng cho cả các luồng miễn phí, được tạo bởi new Thread(...)và các luồng công nhân trong ThreadPoolphản hồi QueueUserWorkItemcác yêu cầu.

Vâng, đó là sự thật, bạn có thể chết đói ThreadPooltrong một quy trình ASP.NET bằng cách xếp hàng quá nhiều mục công việc. Nó sẽ ngăn ASP.NET xử lý các yêu cầu khác. Thông tin trong bài báo là chính xác về mặt đó; cùng một nhóm chủ đề được sử dụng cho QueueUserWorkItemcũng được sử dụng để phục vụ các yêu cầu.

Nhưng nếu bạn thực sự đang xếp hàng đủ các hạng mục công việc để gây ra nạn đói này, thì bạn sẽ chết đói nhóm chủ đề! Nếu bạn đang chạy thực sự hàng trăm hoạt động đòi hỏi nhiều CPU cùng một lúc, thì sẽ tốt gì nếu có một luồng công nhân khác để phục vụ một yêu cầu ASP.NET, khi máy đã quá tải? Nếu gặp trường hợp này, bạn cần thiết kế lại toàn bộ!

Hầu hết thời gian tôi thấy hoặc nghe nói về mã đa luồng được sử dụng không phù hợp trong ASP.NET, nó không phải để xếp hàng đợi công việc đòi hỏi nhiều CPU. Nó dành cho công việc ràng buộc I / O xếp hàng. Và nếu bạn muốn làm I / O hoạt động, thì bạn nên sử dụng luồng I / O (I / O Completion Port).

Cụ thể, bạn nên sử dụng các lệnh gọi lại không đồng bộ được hỗ trợ bởi bất kỳ lớp thư viện nào bạn đang sử dụng. Các phương pháp này luôn được dán nhãn rất rõ ràng; chúng bắt đầu bằng các từ BeginEnd. Như trong Stream.BeginRead, Socket.BeginConnect, WebRequest.BeginGetResponse, và vân vân.

Các phương pháp này sử dụng ThreadPool, nhưng chúng sử dụng IOCP, không can thiệp vào các yêu cầu ASP.NET. Chúng là một loại luồng nhẹ đặc biệt có thể được "đánh thức" bởi một tín hiệu ngắt từ hệ thống I / O. Và trong ứng dụng ASP.NET, bạn thường có một luồng I / O cho mỗi luồng công nhân, vì vậy mỗi yêu cầu đơn lẻ có thể có một hoạt động không đồng bộ được xếp hàng đợi. Đó thực sự là hàng trăm hoạt động không đồng bộ mà không có bất kỳ sự suy giảm hiệu suất đáng kể nào (giả sử hệ thống con I / O có thể theo kịp). Đó là cách nhiều hơn những gì bạn cần.

Chỉ cần lưu ý rằng ủy quyền không đồng bộ không hoạt động theo cách này - họ sẽ kết thúc bằng cách sử dụng một chuỗi công nhân, giống như vậy ThreadPool.QueueUserWorkItem. Chỉ có các phương thức không đồng bộ được tích hợp sẵn của các lớp thư viện .NET Framework có khả năng thực hiện điều này. Bạn có thể tự làm, nhưng nó phức tạp và hơi nguy hiểm và có thể nằm ngoài phạm vi của cuộc thảo luận này.

Câu trả lời tốt nhất cho câu hỏi này, theo ý kiến ​​của tôi, là không sử dụng ThreadPool hoặc một phiên bản nền Threadtrong ASP.NET . Nó hoàn toàn không giống như xoay một chuỗi trong ứng dụng Windows Forms, nơi bạn làm điều đó để giữ cho giao diện người dùng luôn phản hồi và không quan tâm đến hiệu quả của nó. Trong ASP.NET, mối quan tâm của bạn là thông lượng và tất cả ngữ cảnh chuyển đổi trên tất cả các luồng công nhân đó sẽ hoàn toàn giết chết thông lượng của bạn cho dù bạn có sử dụng ThreadPoolhay không.

Xin vui lòng, nếu bạn thấy mình đang viết mã luồng trong ASP.NET - hãy xem xét liệu nó có thể được viết lại để sử dụng các phương thức không đồng bộ có sẵn từ trước hay không và nếu không thể, thì hãy xem xét liệu bạn có thực sự cần mã hay không để chạy trong một chuỗi nền. Trong hầu hết các trường hợp, bạn có thể sẽ thêm phức tạp mà không có lợi ích ròng.


Cảm ơn vì phản hồi chi tiết đó và bạn nói đúng, tôi sẽ thử và sử dụng các phương pháp không đồng bộ khi có thể (cùng với bộ điều khiển không đồng bộ trong ASP.NET MVC). Trong trường hợp ví dụ của tôi, với trình ghi nhật ký từ xa, đây chính xác là những gì tôi có thể làm. Mặc dù vậy, đó là một vấn đề thiết kế thú vị vì nó đẩy việc xử lý không đồng bộ xuống mức thấp nhất trong mã của bạn (tức là triển khai trình ghi nhật ký), thay vì có thể quyết định nó từ cấp bộ điều khiển (trong trường hợp sau , bạn cần, ví dụ, hai triển khai trình ghi nhật ký để có thể lựa chọn).
Michael Hart

@Michael: Các lệnh gọi lại không đồng bộ nói chung khá dễ kết hợp nếu bạn muốn đẩy nó lên nhiều cấp hơn; bạn có thể tạo một mặt tiền xung quanh các phương thức không đồng bộ và bọc chúng bằng một phương thức duy nhất sử dụng Action<T>làm một cuộc gọi lại, chẳng hạn. Nếu ý của bạn là việc lựa chọn sử dụng một luồng công nhân hoặc luồng I / O xảy ra ở mức thấp nhất, đó là chủ ý; chỉ có cấp đó mới có thể quyết định xem nó có cần IOCP hay không.
Aaronaught

Mặc dù, như một điểm đáng quan tâm, chỉ có .NET ThreadPoolmới hạn chế bạn theo cách này, có thể là do họ không tin tưởng các nhà phát triển làm đúng. Nhóm luồng Windows không được quản lý có một API rất giống nhưng thực sự cho phép bạn chọn loại luồng.
Aaronaught

2
Cổng hoàn thành I / O (IOCP). mô tả của IOCP không hoàn toàn đúng. trong IOCP, bạn có một số luồng công nhân tĩnh thay phiên nhau làm việc trên TẤT CẢ các tác vụ đang chờ xử lý. Không nên nhầm lẫn với các nhóm luồng có kích thước cố định hoặc động NHƯNG có một luồng cho mỗi tác vụ - tỷ lệ đáng kinh ngạc. không giống như ASYNC, bạn không có một luồng cho mỗi tác vụ. một luồng IOCP có thể hoạt động một chút trên tác vụ 1, sau đó chuyển sang tác vụ 3, tác vụ 2 rồi quay lại tác vụ 1 một lần nữa. trạng thái phiên tác vụ được lưu và được chuyển giữa các luồng.
MickyD

1
Điều gì về chèn cơ sở dữ liệu? Có lệnh ASYNC SQL (như Execute) không? Chèn cơ sở dữ liệu là về hoạt động I / O chậm nhất xung quanh (vì bị khóa) và việc để luồng chính chờ (các) hàng được chèn chỉ là một sự lãng phí chu kỳ CPU.
Ian Thompson

45

Theo Thomas Marquadt của nhóm ASP.NET tại Microsoft, có thể an toàn khi sử dụng ASP.NET ThreadPool (QueueUserWorkItem).

Từ bài báo :

Q) Nếu Ứng dụng ASP.NET của tôi sử dụng luồng CLR ThreadPool, tôi sẽ không chết đói ASP.NET, vốn cũng sử dụng CLR ThreadPool để thực hiện các yêu cầu? ..

A) Tóm lại, đừng lo lắng về việc chết đói ASP.NET của các luồng, và nếu bạn nghĩ rằng có vấn đề ở đây, hãy cho tôi biết và chúng tôi sẽ giải quyết.

Q) Tôi có nên tạo chủ đề của riêng mình (Chủ đề mới) không? Điều này sẽ không tốt hơn cho ASP.NET, vì nó sử dụng CLR ThreadPool.

A) Xin đừng. Hay nói một cách khác là không !!! Nếu bạn thực sự thông minh — thông minh hơn tôi nhiều — thì bạn có thể tạo chuỗi của riêng mình; nếu không, thậm chí không nghĩ về nó. Dưới đây là một số lý do tại sao bạn không nên thường xuyên tạo chuỗi mới:

  1. Nó rất đắt, so với QueueUserWorkItem ... Nhân tiện, nếu bạn có thể viết ThreadPool tốt hơn CLR, tôi khuyến khích bạn nộp đơn xin việc tại Microsoft, bởi vì chúng tôi chắc chắn đang tìm kiếm những người như bạn !.

4

Các trang web không nên xoay quanh các chủ đề sinh sản.

Bạn thường chuyển chức năng này ra một Dịch vụ Windows mà sau đó bạn giao tiếp với (tôi sử dụng MSMQ để nói chuyện với họ).

-- Biên tập

Tôi đã mô tả một cách triển khai ở đây: Xử lý nền dựa trên hàng đợi trong ứng dụng web ASP.NET MVC

-- Biên tập

Để mở rộng lý do tại sao điều này thậm chí còn tốt hơn chỉ là các chuỗi:

Sử dụng MSMQ, bạn có thể giao tiếp với một máy chủ khác. Bạn có thể ghi vào một hàng đợi giữa các máy, vì vậy nếu bạn xác định, vì một lý do nào đó, tác vụ nền của bạn đang sử dụng quá nhiều tài nguyên của máy chủ chính, bạn có thể thay đổi nó khá tầm thường.

Nó cũng cho phép bạn xử lý hàng loạt bất kỳ tác vụ nào bạn đang cố gắng thực hiện (gửi email / bất cứ thứ gì).


4
Tôi sẽ không đồng ý rằng tuyên bố chung chung này luôn đúng - đặc biệt là đối với các nhiệm vụ không quan trọng. Tạo một Dịch vụ Windows, chỉ cho mục đích ghi nhật ký không đồng bộ chắc chắn có vẻ quá mức cần thiết. Ngoài ra, tùy chọn đó không phải lúc nào cũng khả dụng (có thể triển khai MSMQ và / hoặc Dịch vụ Windows).
Michael Hart

Chắc chắn rồi, nhưng đó là cách 'tiêu chuẩn' để triển khai các tác vụ không đồng bộ từ một trang web (chủ đề hàng đợi so với một số quy trình khác).
Noon Silk

2
Không phải tất cả các tác vụ không đồng bộ đều được tạo như nhau, đó là lý do tại sao các trang không đồng bộ ví dụ tồn tại trong ASP.NET. Nếu tôi muốn tìm nạp một kết quả từ một dịch vụ web từ xa để hiển thị, tôi sẽ không làm điều đó thông qua MSMQ. Trong trường hợp này, tôi đang viết vào nhật ký bằng một bài đăng từ xa. Nó không phù hợp với vấn đề để viết một Dịch vụ Windows, cũng như kết nối MSMQ cho điều đó (và tôi cũng không thể vì ứng dụng cụ thể này có trên Azure).
Michael Hart

1
Hãy xem xét: bạn đang viết thư cho một máy chủ từ xa? Điều gì sẽ xảy ra nếu máy chủ đó bị lỗi hoặc không thể truy cập được? Bạn có muốn thử lại bài viết của mình không? Có thể bạn sẽ làm được, có thể bạn sẽ không. Với cách triển khai của bạn, thật khó để thử lại. Với dịch vụ, nó trở nên khá tầm thường. Tôi đánh giá cao rằng bạn có thể không làm được và tôi sẽ để người khác giải đáp các vấn đề cụ thể về việc tạo chuỗi từ các trang web [tức là nếu chuỗi của bạn không phải là nền, v.v.], nhưng tôi đang phác thảo 'phù hợp' cách để làm điều đó. Tôi không quen thuộc với azure, mặc dù tôi đã sử dụng ec2 (bạn có thể cài đặt hệ điều hành trên đó, vì vậy mọi thứ đều tốt).
Noon Silk

@silky, cảm ơn vì những bình luận. Tôi đã nói "không quan trọng" để tránh giải pháp nặng hơn (nhưng bền) này. Tôi đã làm rõ câu hỏi để rõ ràng là tôi không yêu cầu phương pháp hay nhất xoay quanh các hạng mục công việc được xếp hàng. Azure không hỗ trợ loại kịch bản này (nó có lưu trữ hàng đợi riêng) - nhưng hoạt động xếp hàng quá tốn kém cho việc ghi nhật ký đồng bộ, vì vậy dù sao tôi cũng cần một giải pháp không đồng bộ. Trong trường hợp của tôi, tôi biết những cạm bẫy của sự thất bại, nhưng tôi sẽ không bổ sung thêm cơ sở hạ tầng đề phòng trường hợp nhà cung cấp dịch vụ ghi nhật ký cụ thể này không thành công - tôi cũng có các nhà cung cấp dịch vụ ghi nhật ký khác.
Michael Hart

4

Tôi chắc chắn nghĩ rằng thực tiễn chung cho công việc không đồng bộ nhanh chóng, mức độ ưu tiên thấp trong ASP.NET là sử dụng nhóm luồng .NET, đặc biệt cho các tình huống có lưu lượng truy cập cao khi bạn muốn tài nguyên của mình bị giới hạn.

Ngoài ra, việc triển khai luồng bị ẩn - nếu bạn bắt đầu tạo các luồng của riêng mình, bạn cũng phải quản lý chúng đúng cách. Không nói rằng bạn không thể làm được, nhưng tại sao lại phát minh ra bánh xe đó?

Nếu hiệu suất trở thành vấn đề và bạn có thể thiết lập rằng nhóm luồng là yếu tố giới hạn (chứ không phải kết nối cơ sở dữ liệu, kết nối mạng đi, bộ nhớ, thời gian chờ trang, v.v.) thì bạn tinh chỉnh cấu hình nhóm luồng để cho phép nhiều luồng công nhân hơn, yêu cầu được xếp hàng cao hơn , Vân vân.

Nếu bạn không gặp vấn đề về hiệu suất thì việc chọn tạo ra các luồng mới để giảm bớt sự cạnh tranh với hàng đợi yêu cầu ASP.NET là cách tối ưu hóa quá sớm cổ điển.

Lý tưởng nhất là bạn không cần phải sử dụng một chuỗi riêng để thực hiện thao tác ghi nhật ký - chỉ cần cho phép chuỗi gốc hoàn thành thao tác nhanh nhất có thể, đó là lúc MSMQ và một chuỗi / quy trình tiêu dùng riêng biệt xuất hiện trong bức tranh. Tôi đồng ý rằng điều này nặng hơn và nhiều công việc hơn để thực hiện, nhưng bạn thực sự cần độ bền ở đây - tính không ổn định của hàng đợi chia sẻ, trong bộ nhớ sẽ nhanh chóng làm mất đi sự chào đón của nó.


2

Bạn nên sử dụng QueueUserWorkItem và tránh tạo các luồng mới như cách bạn tránh bệnh dịch. Để có hình ảnh giải thích tại sao bạn sẽ không chết đói ASP.NET, vì nó sử dụng cùng một ThreadPool, hãy tưởng tượng một vận động viên tung hứng rất điêu luyện sử dụng hai tay để giữ nửa tá chốt bowling, kiếm hoặc bất cứ thứ gì trong chuyến bay. Để có hình dung về lý do tại sao việc tạo chuỗi của riêng bạn là không tốt, hãy tưởng tượng điều gì xảy ra ở Seattle vào giờ cao điểm khi lối vào được sử dụng nhiều vào đường cao tốc cho phép các phương tiện tham gia giao thông ngay lập tức thay vì sử dụng đèn và giới hạn số lối vào cứ vài giây một lần . Cuối cùng, để được giải thích chi tiết, vui lòng xem liên kết này:

http://blogs.msdn.com/tmarq/archive/2010/04/14/performing-asynchronous-work-or-tasks-in-asp-net-application.aspx

Cảm ơn, Thomas


Liên kết đó rất hữu ích, cảm ơn vì điều đó Thomas. Tôi cũng muốn nghe bạn nghĩ gì về phản hồi của @ Aaronaught.
Michael Hart

Tôi đồng ý với Aaronaught, và cũng nói như vậy trong bài đăng trên blog của tôi. Tôi nói theo cách này, "Trong nỗ lực đơn giản hóa quyết định này, bạn chỉ nên chuyển [sang một chuỗi khác] nếu bạn muốn chặn chuỗi yêu cầu ASP.NET trong khi bạn không làm gì cả. Đây là đơn giản hóa quá mức, nhưng tôi đang cố gắng để đưa ra quyết định đơn giản. " Nói cách khác, đừng làm điều đó cho công việc tính toán không chặn, nhưng hãy làm điều đó nếu bạn đang thực hiện các yêu cầu dịch vụ web không đồng bộ tới một máy chủ từ xa. Hãy nghe Aaronaught! :)
Thomas

1

Bài báo đó không đúng. ASP.NET có một nhóm các luồng riêng, các luồng công nhân được quản lý, để phục vụ các yêu cầu ASP.NET. Nhóm này thường là một vài trăm luồng và tách biệt với nhóm ThreadPool, là một số bộ xử lý nhỏ hơn.

Sử dụng ThreadPool trong ASP.NET sẽ không can thiệp vào các luồng công nhân ASP.NET. Sử dụng ThreadPool là tốt.

Nó cũng sẽ được chấp nhận để thiết lập một chuỗi đơn chỉ để ghi nhật ký thông báo và sử dụng mẫu nhà sản xuất / người tiêu dùng để chuyển thông điệp nhật ký đến chuỗi đó. Trong trường hợp đó, vì luồng đã tồn tại lâu, bạn nên tạo một luồng mới để chạy ghi nhật ký.

Sử dụng một chuỗi mới cho mọi thư chắc chắn là quá mức cần thiết.

Một giải pháp thay thế khác, nếu bạn chỉ nói về việc ghi nhật ký, là sử dụng một thư viện như log4net. Nó xử lý việc đăng nhập trong một chuỗi riêng biệt và xử lý tất cả các vấn đề ngữ cảnh có thể xảy ra trong kịch bản đó.


1
@Sam, tôi thực sự đang sử dụng log4net và không thấy nhật ký được viết trong một chuỗi riêng - có loại tùy chọn nào mà tôi cần bật không?
Michael Hart

1

Tôi muốn nói rằng bài báo là sai. Nếu bạn đang điều hành một cửa hàng .NET lớn, bạn có thể sử dụng một cách an toàn nhóm trên nhiều ứng dụng và nhiều trang web (sử dụng nhóm ứng dụng riêng biệt), chỉ cần dựa trên một câu lệnh trong tài liệu ThreadPool :

Có một nhóm chủ đề cho mỗi quy trình. Nhóm luồng có kích thước mặc định là 250 luồng công nhân trên mỗi bộ xử lý có sẵn và 1000 luồng hoàn thành I / O. Số luồng trong nhóm luồng có thể được thay đổi bằng cách sử dụng phương thức SetMaxThreads. Mỗi luồng sử dụng kích thước ngăn xếp mặc định và chạy ở mức ưu tiên mặc định.


Một ứng dụng đang chạy trong một quy trình hoàn toàn có khả năng tự đưa nó xuống! (Hoặc ít nhất làm giảm hiệu suất đủ riêng của mình để làm cho thread hồ bơi một đề xuất mất.)
Jeff xương ức

Vì vậy, tôi đoán các yêu cầu ASP.NET sử dụng các luồng hoàn thành I / O (trái ngược với các luồng công nhân) - điều đó có chính xác không?
Michael Hart

Từ bài báo của Fritz Onion, tôi đã liên kết trong câu trả lời của mình: "Mô hình này thay đổi [từ IIS 5.0 sang IIS 6.0] cách xử lý các yêu cầu trong ASP.NET. Thay vì gửi các yêu cầu từ inetinfo.exe tới quy trình ASP.NET worker, http. sys trực tiếp xếp hàng từng yêu cầu trong quy trình thích hợp. Vì vậy, tất cả các yêu cầu hiện được phục vụ bởi các luồng công nhân rút ra từ nhóm luồng CLR và không bao giờ trên các luồng I / O. " (nhấn mạnh của tôi)
Jeff Sternal

Hmmm, tôi vẫn không hoàn toàn chắc chắn ... Bài báo đó là từ tháng 6 năm 2003. Nếu bạn đọc bài báo này từ tháng 5 năm 2004 (phải thừa nhận là vẫn còn khá cũ), nó cho biết "Trang kiểm tra Sleep.aspx có thể được sử dụng để giữ ASP Chuỗi I / O .NET đang bận ", trong đó Sleep.aspx chỉ khiến chuỗi thực thi hiện tại ở chế độ ngủ: msdn.microsoft.com/en-us/library/ms979194.aspx - Khi có cơ hội, tôi sẽ thấy nếu tôi có thể viết mã ví dụ đó và kiểm tra trên IIS 7 và .NET 3.5
Michael Hart

Vâng, văn bản của đoạn văn đó là khó hiểu. Xa hơn trong phần đó, nó liên kết đến một chủ đề hỗ trợ ( support.microsoft.com/default.aspx?scid=kb;EN-US;816829 ) làm rõ mọi thứ: chạy các yêu cầu trên chuỗi hoàn thành I / O là .NET Framework 1.0 sự cố đã được khắc phục trong Gói Cập nhật Hotfix ASP.NET 1.1 tháng 6 năm 2003 (sau đó "TẤT CẢ các yêu cầu hiện chạy trên các luồng của Worker"). Quan trọng hơn, ví dụ đó cho thấy khá rõ ràng rằng nhóm luồng ASP.NET là cùng một nhóm luồng được hiển thị bởi System.Threading.ThreadPool.
Jeff Sternal

1

Tôi đã được hỏi một câu hỏi tương tự tại nơi làm việc vào tuần trước và tôi sẽ cho bạn câu trả lời tương tự. Tại sao bạn lại có các ứng dụng web đa luồng cho mỗi yêu cầu? Máy chủ web là một hệ thống tuyệt vời được tối ưu hóa rất nhiều để cung cấp nhiều yêu cầu kịp thời (tức là đa luồng). Hãy nghĩ về điều gì sẽ xảy ra khi bạn yêu cầu hầu hết mọi trang trên web.

  1. Một yêu cầu được thực hiện cho một số trang
  2. Html được phân phát trở lại
  3. Html yêu cầu khách hàng thực hiện các yêu cầu khác (js, css, hình ảnh, v.v.)
  4. Thông tin khác được cung cấp trở lại

Bạn đưa ra ví dụ về ghi nhật ký từ xa, nhưng đó phải là mối quan tâm của bộ ghi nhật ký của bạn. Cần có một quy trình không đồng bộ để nhận thông báo kịp thời. Sam thậm chí còn chỉ ra rằng trình ghi nhật ký của bạn (log4net) đã hỗ trợ điều này.

Sam cũng đúng ở chỗ việc sử dụng Nhóm luồng trên CLR sẽ không gây ra sự cố với nhóm luồng trong IIS. Tuy nhiên, điều cần quan tâm ở đây là bạn không sinh ra các luồng từ một tiến trình, bạn đang sinh ra các luồng mới từ các luồng IIS threadpool. Có một sự khác biệt và sự phân biệt là quan trọng.

Chủ đề so với Quy trình

Cả luồng và quy trình đều là các phương pháp song song hóa một ứng dụng. Tuy nhiên, các tiến trình là các đơn vị thực thi độc lập chứa thông tin trạng thái riêng của chúng, sử dụng không gian địa chỉ riêng và chỉ tương tác với nhau thông qua cơ chế giao tiếp giữa các tiến trình (thường được quản lý bởi hệ điều hành). Các ứng dụng thường được chia thành các quy trình trong giai đoạn thiết kế và một quy trình chính sinh ra các quy trình con một cách rõ ràng khi nó có ý nghĩa để tách chức năng ứng dụng quan trọng một cách hợp lý. Nói cách khác, quy trình là một cấu trúc kiến ​​trúc.

Ngược lại, một luồng là một cấu trúc mã hóa không ảnh hưởng đến kiến ​​trúc của một ứng dụng. Một quy trình có thể chứa nhiều luồng; tất cả các luồng trong một tiến trình chia sẻ cùng một trạng thái và cùng một không gian bộ nhớ, và có thể giao tiếp với nhau trực tiếp, vì chúng chia sẻ cùng một biến.

Nguồn


3
@Ty, cảm ơn vì đã nhập liệu, nhưng tôi biết rõ về cách hoạt động của máy chủ web và nó không thực sự liên quan đến câu hỏi - một lần nữa, như tôi đã nói trong câu hỏi, tôi không yêu cầu hướng dẫn về điều này như một kiến ​​trúc vấn đề. Tôi yêu cầu thông tin kỹ thuật cụ thể. Đối với vấn đề là "mối quan tâm của trình ghi nhật ký" nên đã có một quy trình không đồng bộ tại chỗ - bạn nghĩ quy trình không đồng bộ đó nên được viết bởi triển khai trình ghi nhật ký như thế nào?
Michael Hart

0

Tôi không đồng ý với bài viết được tham chiếu (C # feeds.com). Nó là dễ dàng để tạo ra một chủ đề mới nhưng nguy hiểm. Số luồng hoạt động tối ưu để chạy trên một lõi thực sự thấp một cách đáng ngạc nhiên - ít hơn 10. Nó quá dễ khiến máy lãng phí thời gian chuyển luồng nếu luồng được tạo cho các tác vụ nhỏ. Chủ đề là một tài nguyên mà YÊU CẦU quản lý. Phần trừu tượng WorkItem ở đó để xử lý điều này.

Ở đây có sự đánh đổi giữa việc giảm số lượng các luồng có sẵn cho các yêu cầu và tạo quá nhiều luồng để cho phép bất kỳ luồng nào trong số chúng xử lý hiệu quả. Đây là một tình huống rất năng động nhưng tôi nghĩ rằng một tình huống nên được quản lý tích cực (trong trường hợp này là nhóm luồng) thay vì để nó cho bộ xử lý để đi trước việc tạo các luồng.

Cuối cùng, bài viết đưa ra một số tuyên bố khá sâu rộng về sự nguy hiểm của việc sử dụng ThreadPool nhưng nó thực sự cần một cái gì đó cụ thể để sao lưu chúng.


0

Việc IIS có sử dụng cùng một ThreadPool để xử lý các yêu cầu gửi đến hay không dường như khó có câu trả lời chính xác và dường như cũng đã thay đổi qua các phiên bản. Vì vậy, có vẻ như không nên sử dụng quá nhiều các luồng ThreadPool, vì vậy IIS có sẵn rất nhiều luồng trong số đó. Mặt khác, tạo chuỗi của riêng bạn cho mỗi nhiệm vụ nhỏ có vẻ là một ý tưởng tồi. Có lẽ, bạn có một số loại khóa trong nhật ký của mình, vì vậy chỉ có một luồng có thể tiến triển tại một thời điểm và phần còn lại sẽ thay phiên nhau được lên lịch và không được lên lịch (chưa kể chi phí tạo một luồng mới). Về cơ bản, bạn gặp phải những vấn đề chính xác mà ThreadPool được thiết kế để tránh.

Có vẻ như một thỏa hiệp hợp lý sẽ là để ứng dụng của bạn phân bổ một chuỗi ghi nhật ký duy nhất mà bạn có thể chuyển tin nhắn đến. Bạn nên cẩn thận rằng việc gửi tin nhắn càng nhanh càng tốt để không làm chậm ứng dụng của mình.


0

Bạn có thể sử dụng Parallel.For hoặc Parallel.ForEach và xác định giới hạn của các luồng có thể mà bạn muốn phân bổ để chạy trơn tru và ngăn chặn tình trạng thiếu nhóm.

Tuy nhiên, khi chạy ở chế độ nền, bạn sẽ cần sử dụng kiểu TPL thuần túy bên dưới trong ứng dụng web ASP.Net.

var ts = new CancellationTokenSource();
CancellationToken ct = ts.Token;

ParallelOptions po = new ParallelOptions();
            po.CancellationToken = ts.Token;
            po.MaxDegreeOfParallelism = 6; //limit here

 Task.Factory.StartNew(()=>
                {                        
                  Parallel.ForEach(collectionList, po, (collectionItem) =>
                  {
                     //Code Here PostLog(logEvent);
                  }
                });
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.