简体   繁体   English

如何使用委托在线程包装器类中传递方法?

[英]How can I use delegates to pass methods in a thread wrapper class?

I'm currently self-teaching myself C# and I'm a bit new at programming so apologies in advance if this is covered in another topic (I tried searching). 我目前正在自学C#,我在编程方面有点新意,如果在另一个主题(我试过搜索)中有所涉及,请提前道歉。

I've been trying to make a generic worker / thread class that takes in a method which specifically wraps around a long set of procedural steps. 我一直在尝试创建一个泛型工作者/线程类,它接受一个专门包含一长串程序步骤的方法。 The idea is to be able to pause/resume it a manner similar to setting breakpoints to pause/unpause in Visual Studio. 我们的想法是能够以类似于在Visual Studio中设置断点以暂停/取消暂停的方式暂停/恢复它。 To provide context, I'm mostly working with automation with an ASP.NET and XAML WPF interface (XAML at the moment). 为了提供上下文,我主要使用ASP.NET和XAML WPF接口(目前是XAML)进行自动化。

My understanding is that I need to use delegates of some sort but I'm looking for a very simple example in plain English. 我的理解是我需要使用某种代表,但我正在寻找一个简单的英语简单示例。 The examples I found are a completely different scope and I have a hard time following the provided solutions in other contexts. 我发现的示例是完全不同的范围,我很难跟踪其他环境中提供的解决方案。

From other examples on MSDN and Stackoverflow, the "Task" worker class is what I have so far, but I'm a bit at a loss on where to on DoDelegatedMethod and my constructor. 从MSDN和Stackoverflow上的其他示例来看,“Task”工作类是我到目前为止所做的,但是我对DoDelegatedMethod和我的构造函数的位置有点不知所措。 What I'm trying to do here is to instantiate a Task object, pass in a delegated method on new instantiation, create a new thread, and marry the passed in method to the thread. 我在这里要做的是实例化一个Task对象,在新实例化上传递一个委托方法,创建一个新线程,并将传入的方法结合到该线程。

The reason why I want a general "Task" is so I can manage specific methods generically instead of having to write a different "DoWork" method for each instance. 我之所以想要一般的“任务”,是因为我可以一般地管理特定的方法,而不必为每个实例编写不同的“DoWork”方法。

Is this the right approach? 这是正确的方法吗?

class Task
{
    private ManualResetEvent _shutdownFlag = new ManualResetEvent(false);
    private ManualResetEvent _pauseFlag = new ManualResetEvent(true);
    private delegate void MyDelegate();

    Thread _thread;

    public Task() { }

    public Task(MyDelegate d = new MyDelegate(DoStuff)) // ERROR
    {
        _thread = new Thread(DoDelegatedMethod); // ERROR
    }

    public void Start()
    {
        _thread.Start();
    }

    public void Resume()
    {
        _pauseFlag.Set(); ;
    }

    public void Stop()
    {
        _shutdownFlag.Set();
        _pauseFlag.Set();
        _thread.Join();
    }

    private void DoDelegatedMethod(MyDelegate d)
    {
        do
        {
            d();
        }
        while (!_shutdownFlag.WaitOne(0));
    }

    // This does nothing but spin forever until I force it to stop
    public void Spin()
    {
        do
        {
            // MessageBox.Show("test");
            _pauseFlag.WaitOne(Timeout.Infinite);
        }
        while (!_shutdownFlag.WaitOne(0));
        //MessageBox.Show("thread over");
    }
}

new Thread() takes a ThreadStart (or ParameterisedThreadStart) argument, and your DoDelegatedMethod callback doesn't have the right signature for ThreadStart. new Thread()接受一个ThreadStart(或ParameterisedThreadStart)参数,而你的DoDelegatedMethod回调没有ThreadStart的正确签名。 So you need to write something like this: 所以你需要写这样的东西:

ThreadStart method = new ThreadStart(() => DoDelegatedMethod(d));
_thread = new Thread(method);

This creates an anonymous callback (the () => DoDelegatedMethod(d) bit) which when run will call DoDelegatedMethod with the delegate d (which is 'captured' by the anonmyous method). 这将创建一个匿名回调( () => DoDelegatedMethod(d)位),在运行时将使用委托d调用DoDelegatedMethod(由anonmyous方法“捕获”)。 Now you pass this anonymous callback to the Thread constructor, so when the thread runs, it will call the anonymous callback, which will in turn call DoDelegatedMethod(d). 现在你将这个匿名回调传递给Thread构造函数,所以当线程运行时,它将调用匿名回调,然后调用DoDelegatedMethod(d)。 Effectively the lambda adapts DoDelegatedMethod to the ThreadStart signature. 实际上,lambda使DoDelegatedMethod适应ThreadStart签名。

Another way to do this would be to change DoDelegatedMethod to take no arguments, and store d as a member field of the Task class which DoDelegateMethod would access. 另一种方法是将DoDelegatedMethod更改为不带参数,并将d存储为DoDelegateMethod将访问的Task类的成员字段。

Also, the reason you get an error on your constructor is that default values can only be of a limited set of types, and delegates aren't one of them (only types that are allowed in attributes, like int, long, string and Type are permitted). 此外,构造函数出错的原因是默认值只能是有限的一组类型,并且委托不是其中之一(只有属性允许的类型,如int,long,string和Type是允许的)。 Use an overload instead: 改为使用过载:

public Task() : this(new MyDelegate(DoStuff)) { ... }
public Task(MyDelegate d) { ... }

Note you may still get an error if DoStuff is an instance method of Task -- it's not clear. 请注意,如果DoStuff是Task的实例方法,您仍可能会收到错误 - 它不清楚。 Personally I think having a default delegate for Task to run is a bit of an odd design, so you may just want to get rid of the default constructor. 我个人认为让Task运行的默认委托是一个奇怪的设计,所以你可能只想摆脱默认的构造函数。


Following the discussion in the comments I thought it was worth summarising the suggested revisions to the Task class: 在评论中讨论后,我认为值得总结一下Task类的建议修订:

public class Task
{
  private readonly Action _action;
  // other members as before

  // default constructor removed

  public Task(Action action)
  {
    _action = action;
  }

  public void Start()
  {
    ThreadStart ts = new ThreadStart(DoDelegatedMethod);
    _thread = new Thread(ts);
    _thread.Start();
  }

  private void DoDelegatedMethod()
  {
    do
    {
      _action();
    }
    while (!_shutdownFlag.WaitOne(0));  
  }

  // other members as before
}

And the usage: 用法:

Task task = new Task(this.AutomatedTasks);
task.Start();

private void AutomatedTasks() { ... }

You may find good implementation of Task Pool manager here www.codeproject.com/KB/threads/smartthreadpool.aspx 您可以在这里找到任务池管理器的良好实现www.codeproject.com/KB/threads/smartthreadpool.aspx

smartthreadpool allows to send in pool any task, but you have to add Pause\\Start functions to it. smartthreadpool允许在池中发送任何任务,但您必须向其添加Pause \\ Start函数。

我会将这个列表模型化,我会像任何其他列表一样枚举,并在枚举器的en中使用'yield'。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM