2013-08-25 30 views
5

Khi chạy qua Service Control Manager, các dịch vụ Windows có cần phải giả định rằng các phương thức xử lý lệnh (OnStart, OnStop, v.v.) có thể được gọi trên các luồng khác nhau không đảm bảo điều đó không.Dịch vụ Windows có cần đảm bảo rằng các lệnh có thể được xử lý trên các luồng khác nhau không?

public class MyService : ServiceBase { 

    private Object _foo;  

    protected override void OnStart(string[] args) { 
     _foo = new Object(); 
    } 

    protected override void OnStop() { 
     if (_foo == null) { 
      throw new Exception("Assignment not visible"); // Can this happen? 
     } 

     _foo = null; 
    } 

} 

tôi không thể tìm thấy một sự đảm bảo rằng các ngoại lệ trong ví dụ của tôi sẽ không được ném, nhưng tất cả các ví dụ tôi đã tìm thấy, trong đó có elsewhere on StackOverflow, dường như cho rằng, ví dụ, bài tập cho các biến trong OnStart() sẽ luôn hiển thị trong OnStop().

Nếu không có bảo đảm nào được thực hiện bởi SCM, tôi biết cách đảm bảo nhiệm vụ được hiển thị (ví dụ: bằng cách thêm khóa xung quanh tất cả lần đọc/ghi trong dịch vụ). Tôi quan tâm đến việc liệu các biện pháp đó có cần thiết hay không.

+1

Tôi cũng quan tâm đến những gì đảm bảo. Tôi chưa bao giờ thấy một cái gì đó giống như ví dụ của bạn gây ra một vấn đề; tuy nhiên, tôi biết rằng có được các nguồn lực gắn liền với nhận dạng luồng (trong trường hợp của chúng tôi là một mutex) đã gây ra vấn đề cho nhóm của tôi trong quá khứ. –

+2

Tôi đã hỏi cùng một câu hỏi trước đây. Tôi sẽ đào nó ra. – spender

+1

Ở đây bạn (đánh dấu là dupe): [Gọi ServiceBase.OnStart và OnStop ... cùng một trường hợp?] (Http: // stackoverflow.com/questions/10799937/call-servicebase-onstart-and-onstop-same-instance) – spender

Trả lời

0

ví dụ bạn đưa ra trong có thể đã xảy ra cũ chỉ rất rất không thể xảy ra của nó:

_foo = new VeryLongAndTimeConsummingTask(); 

[EDIT]: như đã đề cập trong bình luận của SCM ngăn ngừa sự OnStop từ Running trước OnStart hoàn tất. nhận xét này xuất phát từ thực tiễn xấu có thể của tôi về việc hiển thị trình bao bọc bắt đầu và dừng công khai.

nếu sự kiện dừng được gọi trước khi kết thúc mới, điều này có thể xảy ra khi _foo rỗng;

và cũng _foo có thể được phát hành ở vị trí bổ sung trong mã để thực hành tốt để kiểm tra trước.

+0

"nếu sự kiện dừng được gọi trước khi' mới' kết thúc nó có thể xảy ra rằng '_foo' là null;" Không đúng cho mỗi ví dụ trong câu trả lời của tôi. "và cũng' _foo' có thể được phát hành ở một nơi khác trong mã để [đó] là một thực hành tốt để kiểm tra đầu tiên. " Thật. – J0e3gan

+0

@ J0e3gan điểm tốt đẹp tôi giả định OnStart một OnStop có thể được gọi từ một nơi khác nhưng có SCM dos không cho phép bạn làm điều đó một cách ngẫu nhiên. –

2

Trong một nghĩa nào đó, SCM không thể đảm bảo rằng ngoại lệ bạn phác thảo sẽ không được ném. Nó không kiểm soát thao tác của dịch vụ đối với thành viên riêng tư - ví dụ: nếu mã dịch vụ bổ sung ảnh hưởng đến _foo.

Sau khi nói điều này, hãy xem xét kịch bản sau đây để xem lý do tại sao câu trả lời cho câu hỏi cụ thể của bạn rõ ràng là không có:

1) Xây dựng dịch vụ của bạn với những thay đổi sau đây để chứng minh:

public partial class MyService : ServiceBase 
    { 
     private Object _foo; 
     private const string _logName = "MyService Log.txt"; // to support added logging 

     public MyService() 
     { 
      InitializeComponent(); 
     } 

     protected override void OnStart(string[] args) 
     { 
      // demonstrative logging 
      var threadId = Thread.CurrentThread.ManagedThreadId; 
      using (var log = new StreamWriter(AppDomain.CurrentDomain.BaseDirectory + _logName, true)) 
      { 
       log.WriteLine("{0}: In OnStart(string[]) on thread ID {1}. Sleeping for 10 seconds...", DateTime.Now, threadId); 
      } 

      // Sleep before initializing _foo to allow calling OnStop before OnStart completes unless the SCM synchronizes calls to the methods. 
      Thread.Sleep(10000); 

      _foo = new Object(); 
     } 

     protected override void OnStop() 
     { 
      // demonstrative logging added 
      var threadId = Thread.CurrentThread.ManagedThreadId; 
      using (var log = new StreamWriter(AppDomain.CurrentDomain.BaseDirectory + _logName, true)) 
      { 
       log.WriteLine("{0}: In OnStop() on thread ID {1}.", DateTime.Now, threadId); 
      } 

      if (_foo == null) 
      { 
       // demonstrative logging added 
       using (var log = new StreamWriter(AppDomain.CurrentDomain.BaseDirectory + _logName, true)) 
       { 
        log.WriteLine("{0}: _foo == null", DateTime.Now); 
       } 

       throw new Exception("Assignment not visible"); // Can this happen? 
      } 

      _foo = null; 
     } 
    } 

2) Mở một lệnh shell.

3) Mở một trình bao lệnh khác.

4) Trong trình bao lệnh đầu tiên, hãy cài đặt dịch vụ (với sc create) nếu bạn chưa có, và khởi động nó (với net start). Bạn sẽ thấy:

Dịch vụ MyService đang bắt đầu .....

Các dấu chấm dấu nên được bổ sung từng người một như SCM đợi sau 10 giây ngủ để bắt đầu dịch vụ.

5) Trong trình bao lệnh thứ hai, hãy thử dừng dịch vụ (với net stop) trước 10 giây trôi qua. Bạn sẽ thấy:

Dịch vụ đang bắt đầu hoặc dừng. Vui lòng thử lại sau.

Vì vậy, bắt đầu dịch vụ rõ ràng là hoạt động chặn phải hoàn tất trước khi dịch vụ có thể dừng lại.

6) Kiểm tra vỏ lệnh đầu tiên sau 10 giây đã trôi qua. Bạn sẽ thấy:

Dịch vụ MyService đã được bắt đầu thành công.

7) Quay lại trình bao lệnh thứ hai và thử dừng dịch vụ lại. Bạn sẽ thấy:

Dịch vụ MyService đang dừng.

Dịch vụ MyService đã được dừng thành công.

8) Xem lại nhật ký kết quả - ví dụ:

10/22/2013 7:28:55 AM: Trong OnStart (string []) trên thread ID 4. Ngủ trong 10 giây ...

10/22/2013 7:29:17 AM: Trong OnStop() trên thread ID 5.

khởi động và dừng dịch vụ một cách nhanh chóng dễ dàng hơn với hai vỏ lệnh tôi nghĩ; nhưng ví dụ này cũng hoạt động tương tự với một trình bao lệnh.

Cuối cùng, bạn có thể tìm thấy Mitchell Taylor (CoolDadTx) 's câu trả lời cho a similar question in MSDN forums thú vị như tôi đã làm:

Mô hình luồng được sử dụng bởi SCM không chính thức ghi nhận AFAIK. Điều được biết là mỗi dịch vụ được gọi theo chủ đề riêng của nó. Tuy nhiên, SCM có thể hoặc không thể sử dụng một nhóm luồng để tái sử dụng một luồng trên các dịch vụ. Khi một dịch vụ được gọi (bắt đầu, dừng, các lệnh tùy chỉnh, vv), nó được dự kiến ​​sẽ thực hiện nhiệm vụ của nó và quay trở lại một cách nhanh chóng. Có một giới hạn mạnh về việc phải mất bao lâu. Bất cứ điều gì nhiều hơn một sự trở lại nhanh chóng đòi hỏi bạn phải đẩy yêu cầu đến một chủ đề thứ cấp để xử lý. Bản thân SCM chạy trên một luồng riêng biệt, do đó nếu một dịch vụ mất quá nhiều thời gian để phản hồi thì SCM sẽ thấy nó bị treo. Này được thảo luận ở đây: http://msdn.microsoft.com/en-us/library/ms684264(VS.85).aspx

UPDATE:

Riêng lưu ý bài viết Service State Transitions MSDN mà bài viết mà Mitchell Taylor trích dẫn liên kết. Nó chứa một sơ đồ trạng thái khá rõ ràng là & các tài liệu tác giả được xác định chuyển tiếp trạng thái dịch vụ và căn chỉnh với những gì tôi đã nêu ở trên. Nó cũng giải thích liên quan đến sơ đồ trạng thái như thế nào SCM không truyền các yêu cầu điều khiển dịch vụ vào các thời điểm để đảm bảo chỉ các trạng thái chuyển trạng thái được xác định.