Как я могу использовать делегаты для передачи методов в класс-оболочку потока? - PullRequest
7 голосов
/ 11 февраля 2011

Я в настоящее время самообучаю себя C #, и я немного новичок в программировании, поэтому заранее извиняюсь, если это рассматривается в другой теме (я пытался искать).

Я пыталсясоздайте общий класс рабочий / поток, который принимает метод, который определенно охватывает длинный набор процедурных шагов.Идея состоит в том, чтобы иметь возможность приостановить / возобновить его способом, аналогичным установке точек останова для приостановки / возобновления в Visual Studio.Для обеспечения контекста я в основном работаю с автоматизацией с интерфейсом ASP.NET и XAML WPF (на данный момент XAML).

Насколько я понимаю, мне нужно использовать каких-то делегатов, но я ищу очень простой пример на простом английском языке.Примеры, которые я нашел, представляют собой совершенно другую область, и мне трудно следовать предоставленным решениям в других контекстах.

Из других примеров в MSDN и Stackoverflow рабочий класс «Задача» - это то, что я имею до сих пор,но я немного в недоумении о том, где на DoDelegatedMethod и мой конструктор.Здесь я пытаюсь создать экземпляр объекта Task, передать делегированный метод при новом создании, создать новый поток и объединить переданный метод с потоком.

Причина, по которой мне нужна общая «задача», заключается в том, что я могу управлять конкретными методами в общем, вместо того, чтобы писать разные методы «DoWork» для каждого экземпляра.

Это правильный подход??

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");
    }
}

Ответы [ 3 ]

5 голосов
/ 12 февраля 2011

new Thread() принимает аргумент ThreadStart (или ParameterisedThreadStart), а ваш обратный вызов DoDelegatedMethod не имеет правильной подписи для ThreadStart.Поэтому вам нужно написать что-то вроде этого:

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

Это создаст анонимный обратный вызов (бит () => DoDelegatedMethod(d)), который при запуске вызовет DoDelegatedMethod с делегатом d (который «захватывается»анонимный метод).Теперь вы передаете этот анонимный обратный вызов конструктору Thread, поэтому, когда поток запускается, он вызывает анонимный обратный вызов, который, в свою очередь, вызывает DoDelegatedMethod (d).Эффективно, лямбда адаптирует DoDelegatedMethod к подписи ThreadStart.

Другой способ сделать это - изменить DoDelegatedMethod, чтобы он не принимал аргументы, и сохранить d в качестве поля члена класса Task, к которому DoDelegateMethod будет обращаться.1010 *

Кроме того, причина, по которой вы получаете ошибку в своем конструкторе, состоит в том, что значения по умолчанию могут иметь только ограниченный набор типов, и делегаты не являются одним из них (только типы, которые разрешены в атрибутах, например, int,long, string и Type разрешены).Вместо этого используйте перегрузку:

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

Обратите внимание, что вы все равно можете получить ошибку, если DoStuff является экземпляром метода Task - это не ясно.Лично я считаю, что наличие делегата по умолчанию для запуска Task немного странно, поэтому вы можете просто избавиться от конструктора по умолчанию.


После обсуждения в комментариях яЯ подумал, что стоит обобщить предлагаемые изменения в классе 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
}

И использование:

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

private void AutomatedTasks() { ... }
0 голосов
/ 12 февраля 2011

Я бы смоделировал этот Список, который я бы перечислил, как и любой другой список, и использовал бы «yield» в en перечислителя.

0 голосов
/ 11 февраля 2011

Хорошую реализацию диспетчера пула задач вы можете найти здесь:к этому.

...