/// <summary> /// Schedules a worker with a dedicated TCP connection to repeatedly reserve jobs /// from the specified tubes and process them. /// </summary> /// <param name="configuration">The configuration for the Beanstalk connection.</param> /// <param name="options">The worker options.</param> /// <param name="worker">The delegate used to processed reserved jobs.</param> /// <returns>A token which stops the worker when disposed.</returns> public static Task <IDisposable> ConnectWorkerAsync <T>(ConnectionConfiguration configuration, WorkerOptions options, Func <IWorker, Job <T>, Task> worker) { WorkerFunc workerFunc = (w, untypedJob) => { // If the deserializer throws, this will be handled like any other failure of the WorkerFunc. var obj = configuration.JobSerializer.Deserialize <T>(untypedJob.Data); var typedJob = new Job <T>(untypedJob.Id, untypedJob.Data, obj); return(worker(w, typedJob)); }; return(ConnectWorkerAsync(configuration, options, workerFunc)); }
/// <summary>Creates a progress form starting a background thread immediately</summary> public ProgressForm(string title, string desc, Icon icon, ProgressBarStyle style, WorkerFunc func, object arg = null, ThreadPriority priority = ThreadPriority.Normal) : this(title, desc, icon, style) { m_allow_cancel = true; Done = new ManualResetEvent(false); CancelSignal = new ManualResetEvent(false); func = func ?? ((f, o, p) => { // Default worker function, just waits till Cancel is signalled for (; !CancelPending; Thread.Sleep(100)) { p(new UserState { }); } }); // Start the task var dispatcher = Dispatcher.CurrentDispatcher; m_thread = new Thread(new ThreadStart(() => { try { func(this, arg, us => dispatcher.BeginInvoke(new Progress(UpdateProgress), us.Clone())); dispatcher.BeginInvoke(new Progress(UpdateProgress), new UserState { FractionComplete = 1.0 }); } catch (OperationCanceledException) { CancelSignal.Set(); } catch (AggregateException ex) { m_error = ex.InnerExceptions.FirstOrDefault(e => !(e is OperationCanceledException)); if (m_error == null) { CancelSignal.Set(); } } catch (Exception ex) { m_error = ex; } Done.Set(); dispatcher.BeginInvoke(new Progress(UpdateProgress), new UserState { CloseDialog = true }); })) { Name = "ProgressForm", Priority = priority, IsBackground = true, }; m_thread.Start(); }
public ProgressUI(Window?owner, string title, string desc, ImageSource?image, CancellationToken cancel, WorkerFunc worker, object?arg = null, ThreadPriority priority = ThreadPriority.Normal) : this(owner, title, desc, image, cancel) { try { worker ??= DefaultWorkerFunc; void DefaultWorkerFunc(ProgressUI ui, object?a, ProgressCB p) { // Default worker function just waits till cancel is signalled for (; !CancelPending; Thread.Sleep(100)) { p(new UserState { }); } } // Start the worker task m_thread = new Thread(new ThreadStart(ThreadEntry)) { Name = "ProgressUI", Priority = priority, IsBackground = true, }; void ThreadEntry() { var progress_cb = new ProgressCB(UpdateProgress); try { // Run the worker task worker(this, arg, us => Dispatcher.BeginInvoke(progress_cb, us)); Dispatcher.BeginInvoke(progress_cb, new UserState { FractionComplete = 1f }); } catch (Exception ex) { if (ex is OperationCanceledException) { m_cancel.Cancel(); } else { Error = ex; } } // Set the event to say "task done" Done.Set(); Dispatcher.BeginInvoke(progress_cb, new UserState { CloseDialog = true }); } m_thread.Start(); } catch { Dispose(); throw; } }
/// <summary> /// Schedules a worker with a dedicated TCP connection to repeatedly reserve jobs /// from the specified tubes and process them. /// </summary> /// <param name="connectionString">The connection string.</param> /// <param name="options">The worker options.</param> /// <param name="worker">The delegate used to processed reserved jobs.</param> /// <returns>A token which stops the worker when disposed.</returns> public static Task <IDisposable> ConnectWorkerAsync(string connectionString, WorkerOptions options, WorkerFunc worker) { return(ConnectWorkerAsync(ConnectionConfiguration.Parse(connectionString), options, worker)); }
async Task WorkerLoop(WorkerFunc workerFunc, WorkerOptions options, CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { var job = await ReserveAsync(cancellationToken).ConfigureAwait(false); if (job == null) { continue; // DEADLINE_SOON } try { var worker = new Worker(job, this); // Details: http://blog.stephencleary.com/2013/08/startnew-is-dangerous.html await Task.Factory.StartNew( () => workerFunc(worker, job), cancellationToken, TaskCreationOptions.DenyChildAttach, options.TaskScheduler) .Unwrap() .ConfigureAwait(false); if (worker.Completed) { continue; } // else, fall through to the failure handling } catch (Exception) { // Carry on outside the catch... } // Failure handling // Either the workerFunc threw or didn't delete/release/bury the job var cons = this as IConsumer; int priority; switch (options.FailureBehavior) { case WorkerFailureBehavior.Bury: priority = options.FailurePriority ?? (await cons.JobStatisticsAsync(job.Id).ConfigureAwait(false)).Priority; await cons.BuryAsync(job.Id, priority).ConfigureAwait(false); continue; case WorkerFailureBehavior.Release: priority = options.FailurePriority ?? (await cons.JobStatisticsAsync(job.Id).ConfigureAwait(false)).Priority; await cons.ReleaseAsync(job.Id, priority, options.FailureReleaseDelay).ConfigureAwait(false); continue; case WorkerFailureBehavior.Delete: await cons.DeleteAsync(job.Id).ConfigureAwait(false); continue; case WorkerFailureBehavior.NoAction: continue; default: throw new InvalidOperationException("Unhandled WorkerFailureBehavior '" + options.FailureBehavior.ToString() + "'"); } } }
/// <summary> /// Schedules a worker with a dedicated TCP connection to repeatedly reserve jobs /// from the specified tubes and process them. /// </summary> /// <param name="configuration">The configuration for the Beanstalk connection.</param> /// <param name="options">The worker options.</param> /// <param name="worker">The delegate used to processed reserved jobs.</param> /// <returns>A token which stops the worker when disposed.</returns> public static async Task <IDisposable> ConnectWorkerAsync(ConnectionConfiguration configuration, WorkerOptions options, WorkerFunc worker) { // Must capture the context before the first await if (options.TaskScheduler == null) { options.TaskScheduler = SynchronizationContext.Current == null ? TaskScheduler.Default : TaskScheduler.FromCurrentSynchronizationContext(); } var conn = await BeanstalkConnection.ConnectAsync(configuration).ConfigureAwait(false); try { // Just take the default tube if none was given if (options.Tubes.Count > 0) { foreach (var tube in options.Tubes) { await((IConsumer)conn).WatchAsync(tube).ConfigureAwait(false); } if (!options.Tubes.Contains("default")) { await((IConsumer)conn).IgnoreAsync("default").ConfigureAwait(false); } } } catch { conn.Dispose(); throw; } var cts = new CancellationTokenSource(); var disposable = Disposable.Create(() => { cts.Cancel(); cts.Dispose(); conn.Dispose(); }); #pragma warning disable 4014 Task[] workerLoops = new Task[options.NumberOfWorkers]; for (var i = 0; i < options.NumberOfWorkers; i++) { workerLoops[i] = conn.WorkerLoop(worker, options, cts.Token); } // Ensure we handle any thrown exceptions Task.WhenAll(workerLoops).ContinueWith(t => { disposable.Dispose(); if (t.Exception != null) { t.Exception.Handle(ex => true); } }, TaskContinuationOptions.OnlyOnFaulted); #pragma warning restore 4014 return(disposable); }