Asynchronous result descriptor to return to callers

I have a method that queues some jobs that are executed asynchronously. I would like to return some descriptor to the caller, which can be interrogated, wait, or used to get the return value from the operation, but I can not find a class or interface suitable for the task.

BackgroundWorker is approaching, but it focuses on the case when the employee has his own dedicated thread, which is wrong in my case. IAsyncResult looks promising, but the provided AsyncResult implementation is also unsuitable for me. Should I implement IAsyncResult myself?

Explanation

I have a class that conceptually looks like this:

class AsyncScheduler 
{

    private List<object> _workList = new List<object>();
    private bool _finished = false;

    public SomeHandle QueueAsyncWork(object workObject)
    {
        // simplified for the sake of example
        _workList.Add(workObject);
        return SomeHandle;
    }

    private void WorkThread()
    {
        // simplified for the sake of example
        while (!_finished)
        {
            foreach (object workObject in _workList)
            {
                if (!workObject.IsFinished)
                {
                    workObject.DoSomeWork();
                }
            }
            Thread.Sleep(1000);
        }
    }
}

QueueAsyncWork , . QueueAsyncWork - . , ? SomeHandle?

.Net , , . - , workObject.DoSomeWork(). , - , ?

+3
5

, IAsyncResult (, , , ).

public class WorkObjectHandle : IAsyncResult, IDisposable
{
    private int _percentComplete;
    private ManualResetEvent _waitHandle;
    public int PercentComplete {
        get {return _percentComplete;} 
        set 
        {
            if (value < 0 || value > 100) throw new InvalidArgumentException("Percent complete should be between 0 and 100");
            if (_percentComplete = 100) throw new InvalidOperationException("Already complete");
            if (value == 100 && Complete != null) Complete(this, new CompleteArgs(WorkObject));
            _percentComplete = value;
        } 
    public IWorkObject WorkObject {get; private set;}
    public object AsyncState {get {return WorkObject;}}
    public bool IsCompleted {get {return _percentComplete == 100;}}
    public event EventHandler<CompleteArgs> Complete; // CompleteArgs in a usual pattern
    // you may also want to have Progress event
    public bool CompletedSynchronously {get {return false;}}
    public WaitHandle
    {
        get
        {
            // initialize it lazily
            if (_waitHandle == null)
            {
                ManualResetEvent newWaitHandle = new ManualResetEvent(false);
                if (Interlocked.CompareExchange(ref _waitHandle, newWaitHandle, null) != null)
                    newWaitHandle.Dispose();
            }
            return _waitHandle;
        }
    }

    public void Dispose() 
    {
         if (_waitHandle != null)
             _waitHandle.Dispose();
         // dispose _workObject too, if needed
    }

    public WorkObjectHandle(IWorkObject workObject) 
    {
        WorkObject = workObject;
        _percentComplete = 0;
    }
}

public class AsyncScheduler 
{
    private Queue<WorkObjectHandle> _workQueue = new Queue<WorkObjectHandle>();
    private bool _finished = false;

    public WorkObjectHandle QueueAsyncWork(IWorkObject workObject)
    {
        var handle = new WorkObjectHandle(workObject);
        lock(_workQueue) 
        {
            _workQueue.Enqueue(handle);
        }
        return handle;
    }

    private void WorkThread()
    {
        // simplified for the sake of example
        while (!_finished)
        {
            WorkObjectHandle handle;
            lock(_workQueue) 
            {
                if (_workQueue.Count == 0) break;
                handle = _workQueue.Dequeue();
            }
            try
            {
                var workObject = handle.WorkObject;
                // do whatever you want with workObject, set handle.PercentCompleted, etc.
            }
            finally
            {
                handle.Dispose();
            }
        }
    }
}
+1

, (IWorkObject), DoSomeWork. IWorkObject , - , ?

-. Parallel Extension framework (blog). , - :

public void QueueWork(IWorkObject workObject)
{
    Task.TaskFactory.StartNew(() =>
        {
            while (!workObject.Finished)
            {
                int progress = workObject.DoSomeWork();
                DoSomethingWithReportedProgress(workObject, progress);
            }
            WorkObjectIsFinished(workObject);
        });
}

:

QueueUserWorkItem:

public void QueueWork(IWorkObject workObject)
{
    ThreadPool.QueueUserWorkItem(() =>
        {
            while (!workObject.Finished)
            {
                int progress = workObject.DoSomeWork();
                DoSomethingWithReportedProgress(workObject, progress);
            }
            WorkObjectIsFinished(workObject);
        });
}
+1

WorkObject , .

public class WorkObject
{
   public PercentComplete { get; private set; }
   public IsFinished { get; private set; }

   public void DoSomeWork()
   {
      // work done here

      this.PercentComplete = 50;

      // some more work done here

      this.PercentComplete = 100;
      this.IsFinished = true;
   }
}

:

  • , Guid ( ).
  • WorkObject, , Guid, QueueAsyncWork.

, WorkThread ( ); , WorkObject .

private Dictionary<Guid, WorkObject> _workList = 
   new Dictionary<Guid, WorkObject>();

private bool _finished = false;

public Guid QueueAsyncWork(WorkObject workObject)
{
    Guid guid = Guid.NewGuid();
    // simplified for the sake of example
    _workList.Add(guid, workObject);
    return guid;
}

private void WorkThread()
{
    // simplified for the sake of example
    while (!_finished)
    {
        foreach (WorkObject workObject in _workList)
        {
            if (!workObject.IsFinished)
            {
                workObject.DoSomeWork();
            }
        }
        Thread.Sleep(1000);
    }
}

// an example of getting the WorkObject property
public int GetPercentComplete(Guid guid)
{
   WorkObject workObject = null;
   if (!_workList.TryGetValue(guid, out workObject)
      throw new Exception("Unable to find Guid");

   return workObject.PercentComplete;
}
+1

. , string DoSomeWork(int). , :

Func<int, string> myDelegate = DoSomeWork;

BeginInvoke :

int parameter = 10;
myDelegate.BeginInvoke(parameter, Callback, null);

Callback . :

void Callback(IAsyncResult result)
{
    var asyncResult = (AsyncResult) result;
    var @delegate = (Func<int, string>) asyncResult.AsyncDelegate;
    string methodReturnValue = @delegate.EndInvoke(result);
}

, . URL, .

,

0

, WaitHandle, ManualResetEvent:

public abstract class WorkObject : IDispose
{
    ManualResetEvent _waitHandle = new ManualResetEvent(false);

    public void DoSomeWork()
    {
        try
        {
            this.DoSomeWorkOverride();
        }
        finally
        {
            _waitHandle.Set();
        }
    }

    protected abstract DoSomeWorkOverride();

    public void WaitForCompletion()
    {
        _waitHandle.WaitOne();
    }

    public void Dispose()
    {
        _waitHandle.Dispose();
    }
}

using (var workObject = new SomeConcreteWorkObject())
{
    asyncScheduler.QueueAsyncWork(workObject);
    workObject.WaitForCompletion();
}

Dispose workObject.

You can always use alternative implementations that create such a wrapper for each work object and which call _waitHandle.Dispose () in WaitForCompletion (), you can lazily create an instance of the wait descriptor (carefully: race conditions are ahead), etc. (This is pretty much what BeginInvoke does for delegates.)

0
source

Source: https://habr.com/ru/post/1715115/


All Articles