2013-02-27 14 views
6

Ich muss Warteschlange erstellen und mit BackgroundWorker verwenden. So kann ich Operationen hinzufügen und wenn es fertig ist, beginne ich im Hintergrund. Ich fand diesen Code von Google:Erstellen von BackgroundWorker mit Warteschlange

public class QueuedBackgroundWorker<T> 
{ 
    public void QueueWorkItem(
     Queue queue, 
     T inputArgument, 
     Func<T> doWork, 
     Action workerCompleted) 
    { 
     if (queue == null) throw new ArgumentNullException("queue"); 

     BackgroundWorker bw = new BackgroundWorker(); 
     bw.WorkerReportsProgress = false; 
     bw.WorkerSupportsCancellation = false; 
     bw.DoWork += (sender, args) => 
     { 
      if (doWork != null) 
      { 
       args.Result = doWork(new DoWorkArgument<T>((T)args.Argument)); 
      } 
     }; 
     bw.RunWorkerCompleted += (sender, args) => 
     { 
      if (workerCompleted != null) 
      { 
       workerCompleted(new WorkerResult<T>((T)args.Result, args.Error)); 
      } 
      queue.Dequeue(); 
      if (queue.Count > 0) 
      { 
       QueueItem<T> nextItem = queue.Peek() as QueueItem<T>; 
       nextItem.BackgroundWorker.RunWorkerAsync(nextItem.Argument); 
      } 
     }; 

     queue.Enqueue(new QueueItem<T>(bw, inputArgument)); 
     if (queue.Count == 1) 
     { 
      QueueItem<T> nextItem = queue.Peek() as QueueItem<T>; 
      nextItem.BackgroundWorker.RunWorkerAsync(nextItem.Argument); 
     } 
    } 
} 

public class DoWorkArgument<T> 
{ 
    public DoWorkArgument(T argument) 
    { 
     this.Argument = argument; 
    } 
    public T Argument { get; private set; } 
} 

public class WorkerResult<T> 
{ 
    public WorkerResult(T result, Exception error) 
    { 
     this.Result = result; 
     this.Error = error; 
    } 

    public T Result { get; private set; } 
    public Exception Error { get; private set; } 
} 

public class QueueItem<T> 
{ 
    public QueueItem(BackgroundWorker backgroundWorker, T argument) 
    { 
     this.BackgroundWorker = backgroundWorker; 
     this.Argument = argument; 
    } 

    public T Argument { get; private set; } 
    public BackgroundWorker BackgroundWorker { get; private set; } 
} 

Aber ich habe ein Problem mit doWork und WorkerCompleted. Ich bekomme Fehler:

Delegate 'Func' does not take 1 arguments

Wie kann ich das beheben? Wie sollte ich Parameter ändern? Dank

+1

Mindestens auf die Zeile. Und vielleicht nur diese Zeile posten. TL; DR –

Antwort

20

Hier ist ein viel kürzeren Verfahren, das tut, was Sie wollen:

public class BackgroundQueue 
{ 
    private Task previousTask = Task.FromResult(true); 
    private object key = new object(); 
    public Task QueueTask(Action action) 
    { 
     lock (key) 
     { 
      previousTask = previousTask.ContinueWith(t => action() 
       , CancellationToken.None 
       , TaskContinuationOptions.None 
       , TaskScheduler.Default); 
      return previousTask; 
     } 
    } 

    public Task<T> QueueTask<T>(Func<T> work) 
    { 
     lock (key) 
     { 
      var task = previousTask.ContinueWith(t => work() 
       , CancellationToken.None 
       , TaskContinuationOptions.None 
       , TaskScheduler.Default); 
      previousTask = task; 
      return task; 
     } 
    } 
} 

Durch das Hinzufügen jede neue Aktion als Fortsetzung der vorherig Sie sicherstellen, dass nur ein zu einer Zeit gearbeitet, als nächster Punkt Es wird nicht gestartet, bis das vorherige Element fertig ist, Sie stellen sicher, dass kein Thread im Leerlauf ist, wenn nichts bearbeitet werden muss, und Sie stellen sicher, dass alle in der richtigen Reihenfolge ausgeführt werden.

Beachten Sie auch, dass Sie alle Mitglieder static machen können, wenn Sie immer nur eine Warteschlange und keine Nummer benötigen, aber das liegt an Ihnen.

+0

Sieht besser aus, danke dafür. Jetzt verwende ich es wie folgt: 'bgQueue.QueueTask (() => CreateConvertingProcess (item.FullName));'. Können Sie mir helfen, wie kann ich Methode aufrufen, wenn eine Aufgabe erledigt ist? Sollte ich es nach dem Schloss hinzufügen? Ich meine, wenn ich Methode hinzufügen möchte, die ich zu RunWorkerCompleted hinzufügen würde. –

+0

Vielen Dank, es funktioniert. Ich muss lernen, mit Task zu arbeiten. –

+0

Kannst du mir bitte noch einmal helfen? Wie kann ich herausfinden, ob die Warteschlange leer ist? –

0

Offenbar fehlt der zweite generische Parameter - Tout;

Der folgende Code sollte sich darum kümmern:

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 

public static class QueuedBackgroundWorker 
{ 
    public static void QueueWorkItem<Tin, Tout>(
     Queue<QueueItem<Tin>> queue, 
     Tin inputArgument, 
     Func<DoWorkArgument<Tin>, Tout> doWork, 
     Action<WorkerResult<Tout>> workerCompleted) 
    { 
     if (queue == null) throw new ArgumentNullException("queue"); 

     BackgroundWorker bw = new BackgroundWorker(); 
     bw.WorkerReportsProgress = false; 
     bw.WorkerSupportsCancellation = false; 
     bw.DoWork += (sender, args) => 
      { 
       if (doWork != null) 
       { 
        args.Result = doWork(new DoWorkArgument<Tin>((Tin)args.Argument)); 
       } 
      }; 
     bw.RunWorkerCompleted += (sender, args) => 
      { 
       if (workerCompleted != null) 
       { 
        workerCompleted(new WorkerResult<Tout>((Tout)args.Result, args.Error)); 
       } 
       queue.Dequeue(); 
       if (queue.Count > 0) 
       { 
        QueueItem<Tin> nextItem = queue.Peek(); // as QueueItem<T>; 
        nextItem.BackgroundWorker.RunWorkerAsync(nextItem.Argument); 
       } 
      }; 

     queue.Enqueue(new QueueItem<Tin>(bw, inputArgument)); 
     if (queue.Count == 1) 
     { 
      QueueItem<Tin> nextItem = queue.Peek() as QueueItem<Tin>; 
      nextItem.BackgroundWorker.RunWorkerAsync(nextItem.Argument); 
     } 
    } 
} 

public class DoWorkArgument<T> 
{ 
    public DoWorkArgument(T argument) 
    { 
     this.Argument = argument; 
    } 

    public T Argument { get; private set; } 
} 

public class WorkerResult<T> 
{ 
    public WorkerResult(T result, Exception error) 
    { 
     this.Result = result; 
     this.Error = error; 
    } 

    public T Result { get; private set; } 

    public Exception Error { get; private set; } 
} 

public class QueueItem<T> 
{ 
    public QueueItem(BackgroundWorker backgroundWorker, T argument) 
    { 
     this.BackgroundWorker = backgroundWorker; 
     this.Argument = argument; 
    } 

    public T Argument { get; private set; } 

    public BackgroundWorker BackgroundWorker { get; private set; } 
} 

Und die Nutzung sein sollte:

private readonly Queue<QueueItem<int>> _workerQueue = new Queue<QueueItem<int>>(); 
    private int _workerId = 1; 

    [Test] 
    public void BackgroundTest() 
    { 
     QueuedBackgroundWorker.QueueWorkItem(
      this._workerQueue, 
      this._workerId++, 
      args => // DoWork 
       { 
        var currentTaskId = args.Argument; 
        var now = DateTime.Now.ToLongTimeString(); 
        var message = string.Format("DoWork thread started at '{0}': Task Number={1}", now, currentTaskId); 
        return new { WorkerId = currentTaskId, Message = message }; 
       }, 
      args => // RunWorkerCompleted 
       { 
        var currentWorkerId = args.Result.WorkerId; 
        var msg = args.Result.Message; 

        var now = DateTime.Now.ToShortTimeString(); 
        var completeMessage = string.Format(
         "RunWorkerCompleted completed at '{0}'; for Task Number={1}, DoWork Message={2}", 
         now, 
         currentWorkerId, 
         msg); 
       } 
      ); 
    } 
Verwandte Themen