2009-09-22 49 views
4

我有一个相当复杂的多线程Windows服务工作,但我无法弄清楚如何正确清理。下面是一些[伪]代码来显示我的。实际的代码要复杂得多,在这里复制/粘贴可能太多了。正确处理线程的类

基本上,我有一个类创建一个线程来完成这项工作的请求。当一个新的请求进入Listener时,它将它发送给处理器,处理器创建新的请求并维护请求列表。如果服务停止,我将清除列表中的所有请求。但是,当请求工作完成后,我该如何清理该类的一个实例?

感谢您的帮助!

尼尔森

class Service 
{ 
    Listener listener; 
    Processor processor; 

    OnStart() 
    { 
    processor = new Processor(); 
    listener = new Listener(processor); 
    } 

    OnStop() 
    { 
    listener.Dispose(); 
    processor.Dispose(); 
    } 
} 

class Listener 
{ 
    Thread thread; 
    bool terminate = false; 

    Listener(Processor processor) 
    { 
    thread = new Thread(DoWork); 
    thread.Start(processor); 
    } 

    DoWork(Processor processor) 
    { 
    WaitForConnection(NewConnection); 
    } 

NewConnection(String data) 
{ 
    processor.NewRequest(data); 

    if (terminate) 
     return; 

    WaitForConnection(NewConnection); 
} 

    Dispose() 
    { 
    terminate = true; 
    thread.Join(); 
    } 
} 

class Processor 
{ 
    //I need to maintain this list so that when the service stops I can cleanly close down 
    List<Request> requests = new List<Request>(); 

    NewRequest(string data) 
    { 
    request.Add(new Request(data)); 
    } 

    Dispose() 
    { 
    //Cleanup each request 
    foreach (Request request in requests) 
    { 
     request.Dispose(); 
    } 
    } 
} 

class Request 
{ 
    Thread thread; 
    bool terminate; 

    Request(string data) 
    { 
    while (true) 
    { 
     //Do some work 
     Thread.Sleep(1000); 

     if (doneWorking) 
     break; 

     if (terminate) 
     return; 
    } 

    //We're done. If I return this thread stops. But how do I properly remove this Request instance from the Processor.requests list? 
    } 

    Dispose() 
    { 
    terminate = true; 
    thread.Join(); 
    } 
} 

回答

3

这是一个粗略的草图:

delegate void CompletedRequest(Request req); 

class Processor : ITrackCompletion 
{ 
    //I need to maintain this list so that when the service stops I can cleanly close down 
    List<Request> requests = new List<Request>(); 

    public void NewRequest(string data) 
    { 
     lock(requests) 
      request.Add(new Request(data), Complete); 
    } 

    public void Complete(Request req) 
    { 
     lock (requests) 
      requests.Remove(req); 
    } 

    public void Dispose() 
    { 
     //Cleanup each request 
     foreach (Request request in requests.ToArray()) 
     { 
      request.Dispose(); 
     } 
    } 
} 

class Request 
{ 
    Thread thread; 
    bool terminate; 

    public Request(string data, CompletedRequest complete) 
    { 
     try 
     { 
      while (true) 
      { 
       //Do some work 
       Thread.Sleep(1000); 

       if (doneWorking) 
        break; 

       if (terminate) 
        return; 
      } 
     } 
     finally 
     { 
      //We're done. If I return this thread stops. But how do I properly remove this Request instance from the Processor.requests list? 
      complete(this); 
     } 
    } 

    void Dispose() 
    { 
     terminate = true; 
     thread.Join(); 
    } 
} 
+0

这基本上是我现在拥有的。感谢您的确认。为什么要做requests.ToArray()?是否可以避免锁定?以任何方式更快/更安全?谢谢。 – 2009-09-22 20:41:45

+0

更安全,性能更好...锁定是不可能的,因为其他线程将从我的列表中删除它,如果我锁定列表,Join()永远不会完成。在锁定期间不要从功能中调出功能是一种最佳做法。此外,我不能简单地忽略该锁,因为该集合将被修改,并且我的foreach循环会得到一个异常。 – 2009-09-22 23:36:30

+0

我在代码的另一部分获得了第一手的经验。我发现这是因为锁定()造成的,但是感谢你,我能够轻松解决它。 – 2009-09-28 21:16:40

4

一种可能性是回调传递到委托的形式的请求:“当你完成处理,叫我回来告诉我。”然后,只需在请求处理结束时执行回调,并让它处理清理。

但需要注意的一件事情是:如果您尝试通过列表处理事件,然后尝试从另一个线程的列表中删除项目,则会出现问题。您应该保留一个标志(以线程安全的方式访问),并且一旦开始处理列表中的所有内容,请忽略您获得的任何回调。

+0

是委托依赖于处理器的特定实例吗?换句话说,如果我创建了多个Processor实例,并且将一个委托传递给Request(对于Processor中的一个方法),那么当我调用委托时,它是否使用具有正确列表的相同实例? 如果是这样,那是我错过了。我有方法,委托,但我不能(可能不应该,即使我可以)直接跨类使用委托。 – 2009-09-22 19:52:18

+0

关于线程安全,不会简单的锁定()解决问题吗?如果我尝试从列表中删除一个项目并将Dispose()击败我,则该foreach不会找到任何项目。反之亦然,该项目将被删除,然后Dispose()将清理其余部分。 我会试一试,让你知道。再次感谢。 – 2009-09-22 19:54:06

+0

你需要锁定整个'foreach'循环 - 这实际上并没有多大用处,因为在完成时你不再关心。例如,您可以准确决定代表的具体内容 - 例如,每个请求都可能具体。匿名方法或lambda表达式在这里很有帮助。 – 2009-09-22 19:58:17