2016-04-21 21 views
7

Câu hỏi ngắn:
Có ai khác gặp sự cố khi sử dụng đơn NET HttpClient nơi ứng dụng chốt bộ xử lý ở mức 100% cho đến khi khởi động lại không?.NET: Sử dụng CPU 100% trong HttpClient vì từ điển?

Chi tiết:
Tôi đang chạy Dịch vụ Windows liên tục ETL theo lịch biểu. Một trong các luồng đồng bộ hóa dữ liệu đôi khi chỉ chết hoặc bắt đầu chạy ngoài tầm kiểm soát và chốt bộ xử lý ở mức 100%.

Tôi đã may mắn thấy điều này xảy ra trực tiếp trước khi ai đó chỉ cần khởi động lại dịch vụ (sửa tiêu chuẩn) và có thể lấy tệp kết xuất.

Tải tệp này trong WinDbg (w/SOS và SOSEX), tôi thấy rằng tôi có khoảng 15 chủ đề (các nhiệm vụ phụ của chuỗi xử lý chính) tất cả đang chạy với các dấu vết ngăn xếp giống hệt nhau. Tuy nhiên, có vẻ như không có bất kỳ deadlocks. I E. các luồng sử dụng cao đang chạy, nhưng không bao giờ kết thúc.

Các liên quan stack-trace phân khúc sau (địa chỉ bỏ qua):

System.Collections.Generic.Dictionary`2[[System.__Canon, mscorlib],[System.__Canon, mscorlib]].FindEntry(System.__Canon) 
System.Collections.Generic.Dictionary`2[[System.__Canon, mscorlib],[System.__Canon, mscorlib]].TryGetValue(System.__Canon, System.__Canon ByRef) 
System.Net.Http.Headers.HttpHeaders.ContainsParsedValue(System.String, System.Object) 
System.Net.Http.Headers.HttpGeneralHeaders.get_TransferEncodingChunked() 
System.Net.Http.Headers.HttpGeneralHeaders.AddSpecialsFrom(System.Net.Http.Headers.HttpGeneralHeaders) 
System.Net.Http.Headers.HttpRequestHeaders.AddHeaders(System.Net.Http.Headers.HttpHeaders) 
System.Net.Http.HttpClient.SendAsync(System.Net.Http.HttpRequestMessage, System.Net.Http.HttpCompletionOption, System.Threading.CancellationToken) 
... 
[Our Application Code] 

Theo this article (và những người khác tôi đã tìm thấy), việc sử dụng từ điển là không thread-an toàn, và vô hạn vòng có thể (như là các sự cố liên tiếp) nếu bạn truy cập từ điển theo cách đa luồng.

NHƯNG mã ứng dụng của chúng tôi không sử dụng từ điển một cách rõ ràng. Vậy từ điển được đề cập trong ngăn xếp theo dõi ở đâu?

Làm theo thông qua .NET Reflector, nó xuất hiện rằng HttpClient sử dụng từ điển để lưu trữ bất kỳ giá trị nào đã được định cấu hình trong thuộc tính "DefaultRequestHeaders". Bất kỳ yêu cầu nào được gửi qua HttpClient, do đó, kích hoạt một liệt kê một từ điển đơn lẻ, không an toàn (để thêm các tiêu đề mặc định vào yêu cầu), có khả năng quay vô hạn (hoặc giết) các chủ đề liên quan nếu một tham nhũng xảy ra.

Microsoft đã tuyên bố thẳng thắn rằng lớp HttpClient là an toàn chỉ. Nhưng có vẻ như với tôi như thế này là không còn đúng nếu bất kỳ tiêu đề đã được thêm vào DefaultRequestHeaders của HttpClient.

Phân tích của tôi dường như chỉ ra rằng đây là vấn đề gốc thực, và cách giải quyết đơn giản là không bao giờ sử dụng DefaultRequestHeaders nơi HttpClient có thể được sử dụng theo cách đa luồng.

Tuy nhiên, tôi đang tìm một số xác nhận rằng tôi không sủa cây sai. Nếu điều này là chính xác, nó có vẻ giống như một lỗi trong khung công tác .NET, mà tôi tự động có xu hướng nghi ngờ.

Xin lỗi vì câu hỏi dài dòng, nhưng cảm ơn mọi đầu vào bạn có thể có.

+0

đây là tiêu chuẩn spiel của họ với chủ đề an toàn: "Bất kỳ công cộng tĩnh (được chia sẻ trong Visual Basic) thành viên của loại này là chủ đề an toàn. Bất kỳ thành viên dụ không được đảm bảo để được an toàn thread." –

+0

vui lòng cung cấp [mcve] –

+1

Tại sao làm cho ứng dụng khách của khách hàng trở thành một singleton? Dường như với tôi, bản sửa lỗi dễ nhất sẽ đơn giản là để mỗi thread tạo và sử dụng cá thể riêng của nó. – pquest

Trả lời

1

Cảm ơn tất cả các nhận xét; họ đã cho tôi suy nghĩ theo các dòng khác nhau, và giúp tôi tìm ra nguyên nhân gốc rễ cuối cùng của vấn đề.

Mặc dù vấn đề hậu quả của tham nhũng trong từ điển ủng hộ của DefaultRequestHeaders, thủ phạm thực sự là mã khởi tạo cho các đối tượng HttpClient:

private HttpClient InitializeClient() 
{ 
    if (_client == null) 
    { 
     _client = GetHttpClient(); 
     _client.DefaultRequestHeaders.Accept.Clear(); 
     _client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); 
     SetBaseAddress(BaseAddress); 
    } 
    return _client; 
} 

tôi đã nói rằng HttpClient là một singleton, đó là một phần không chính xác. Nó được tạo ra như một cá thể đơn được chia sẻ giữa nhiều luồng làm một đơn vị công việc và được xử lý khi công việc hoàn tất. Một cá thể mới sẽ được kéo dài trong lần tiếp theo nhiệm vụ cụ thể này phải được thực hiện.

Phương thức "InitializeClient" ở trên được gọi mỗi lần yêu cầu được gửi và chỉ ngắn mạch do trường "_client" không bị vô hiệu sau lần chạy đầu tiên.

(Lưu ý rằng điều này không được thực hiện trong hàm tạo của đối tượng vì nó là lớp trừu tượng và "GetHttpClient" là phương thức trừu tượng - BTW: không bao giờ gọi phương thức trừu tượng trong hàm tạo của lớp cơ sở. .. gây ra các cơn ác mộng khác)

Tất nhiên, rõ ràng đây không phải là chủ đề an toàn và hành vi kết quả là không xác định.

Khắc phục là đặt mã này sau câu lệnh "khóa" được kiểm tra hai lần (mặc dù tôi sẽ loại bỏ việc sử dụng thuộc tính "DefaultRequestHeaders", chỉ vì).

Tóm lại, câu hỏi ban đầu của tôi không bao giờ là vấn đề nếu bạn cẩn thận trong cách bạn khởi tạo HttpClient.

Cảm ơn bạn đã suy nghĩ rõ ràng rằng tất cả các bạn đã cung cấp!

+0

Đây là một vấn đề về luồng cổ điển. Bảng băm sẽ trở thành cyclic do các cuộc đua. Một vụ va chạm vô hạn để nói. Tôi không thấy câu hỏi của bạn hoặc tôi đã trả lời. – usr

+0

@usr: Không phải lo lắng; Tôi chỉ vui mừng khi nhận được một lời giải thích tốt là nguyên nhân. :) – Greenknight

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