4

考虑以下:

public class SomeService
{
    public Task StartAsync()
    {
        return Task.Factory
            .StartNew(() => DoStartup());
    }

    public Task StopAsync()
    {
        return Task.Factory
            .StartNew(() => DoShutdown());
    }
}

上面的问题是,如果多次调用其中一个StartAsyncStopAsync多个,那么将创建多个任务来启动/停止服务。实际上,任何时候都只希望有一个启动/停止任务处于活动状态。为此,我通常会这样做:

public class SomeService
{
    private readonly object startSync = new object();
    private readonly object stopSync = new object();
    private Task startTask;
    private Task stopTask;

    public Task StartAsync()
    {
        var startTaskLocal = this.startTask;

        if (startTaskLocal != null)
        {
            return startTaskLocal;
        }

        lock (this.startSync)
        {
            if (this.startTask != null)
            {
                return this.startTask;
            }

            this.startTask = Task.Factory
                .StartNew(() => DoStartup())
                .Then(x =>
                    {
                        lock (this.stopSync)
                        {
                            this.stopTask = null);
                        }
                    });

            return this.startTask;
        }
    }

    public Task StopAsync()
    {
        // similar pattern to above
    }
}

现在虽然这有效,但它有点难看。在我试图将这种模式封装成一些小而可重用的东西之前,我想知道是否已经建立了一种我不知道的方法?

PS。StartAsync我通过等待任何未完成的呼叫来进一步扩展这个想法StopAsync,反之亦然。这意味着在任何时候只能进行一个启动或停止操作。这很容易通过任务组合来完成。如果我可以首先找出是否有一种既定的方法可以巧妙地实现上述目标,那么我就可以确定它是否适合这种扩展行为。

4

4 回答 4

1

您可以使用状态机模式。当代码进入 AsyncStart 时,将 state 设置为Starting. 我推荐一个回调,将对象的状态设置为Running.

Stopping为,提供相同的值Stopped

因此,在您的服务内部,您可能有一个ServiceState对象。在调用AsyncStart/之前AsyncStop,请检查当前状态。

更新
...另一种方法可能是存储和检查Cancellation Token。有无数关于如何使用CancellationToken. 这只是我的想法,并不能确认这一定是最好的方法。

于 2012-07-24T16:03:34.073 回答
1

我终于开始在我的代码库中解决这个问题,并想在这里分享我的解决方案。快速解释:我有一个StateMachineTaskFactory<T>类,其中T定义了有效状态(通常是枚举)。此任务工厂允许您注册有效的转换(例如,在转换到Started状态时,在转换进行时使用Starting)并制定转换。它在维护异步 API 的同时保证状态机语义。它基本上形式化了我原始代码中存在的状态机,以一种健壮且可重用的方式进行。

首先,这是一个如何在我的问题中提出的用例中使用它的示例:

public enum ServiceState
{
    Uninitialized,
    Initializing,
    Initialized,
    Starting,
    Started,
    Stopping,
    Stopped
}

public class SomeService
{
    private readonly StateMachineTaskFactory<ServiceState> stateMachineTaskFactory;

    public Service()
    {
        this.stateMachineTaskFactory = new StateMachineTaskFactory<ServiceState>();
        this.stateMachineTaskFactory.RegisterTransition(ServiceState.Initializing, ServiceState.Initialized, this.OnInitializeAsync);
        this.stateMachineTaskFactory.RegisterTransition(ServiceState.Starting, ServiceState.Started, this.OnStartAsync);
        this.stateMachineTaskFactory.RegisterTransition(ServiceState.Stopping, ServiceState.Stopped, this.OnStopAsync);
    }

    // we don't support cancellation in our initialize API
    public Task InitializeAsync()
    {
        return this.stateMachineTaskFactory.TransitionTo(ServiceState.Initialized);
    }

    public Task StartAsync(CancellationToken cancellationToken = default(CancellationToken))
    {
        return this.stateMachineTaskFactory.TransitionTo(ServiceState.Started, cancellationToken);
    }

    public Task StopAsync(CancellationToken cancellationToken = default(CancellationToken))
    {
        return this.stateMachineTaskFactory.TransitionTo(ServiceState.Stopped, cancellationToken);
    }

    // even though we don't support cancellation during initialization, we'll still get a cancellation token, but it will CancellationToken.None
    private Task OnInitializeAsync(CancellationToken cancellationToken, object state)
    {
        // return a Task that performs the actual work involved in initializing
    }

    private Task OnStartAsync(CancellationToken cancellationToken, object state)
    {
        // return a Task that performs the actual work involved in starting, passing on the cancellation token as relevant
    }

    private Task OnStopAsync(CancellationToken cancellationToken, object state)
    {
        // return a Task that performs the actual work involved in stopping, passing on the cancellation token as relevant
    }
}

可用的功能和灵活性比上述示例用法中的明显更多,但这可能是正常的用例。

对不起,下面的代码墙。我删除了 API 文档以提高可读性。有几个实用程序类我没有包括在内,但它们是不言自明的。

[Serializable]
public sealed class StateTransitionForbiddenException<T> : InvalidOperationException
    where T : struct
{
    private readonly T targetState;
    private readonly T state;

    public StateTransitionForbiddenException()
    {
    }

    public StateTransitionForbiddenException(string message)
        : base(message)
    {
    }

    public StateTransitionForbiddenException(string message, Exception innerException)
        : base(message, innerException)
    {
    }

    public StateTransitionForbiddenException(T targetState, T state)
        : base("A transition to state '" + targetState + "' was forbidden by the validate transition callback.")
    {
        this.targetState = targetState;
        this.state = state;
    }

    public StateTransitionForbiddenException(string message, T targetState, T state)
        : base(message)
    {
        this.targetState = targetState;
        this.state = state;
    }

    private StateTransitionForbiddenException(SerializationInfo info, StreamingContext context)
        : base(info, context)
    {
        this.targetState = (T)info.GetValue("TargetState", typeof(T));
        this.state = (T)info.GetValue("State", typeof(T));
    }

    public T TargetState
    {
        get { return this.targetState; }
    }

    public T State
    {
        get { return this.state; }
    }

    public override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        base.GetObjectData(info, context);

        info.AddValue("TargetState", this.targetState);
        info.AddValue("State", this.state);
    }
}

[DebuggerDisplay("{OldState} -> {NewState}")]
public sealed class StateChangedEventArgs<T> : EventArgs
    where T : struct
{
    private readonly T oldState;
    private readonly T newState;

    public StateChangedEventArgs(T oldState, T newState)
    {
        this.oldState = oldState;
        this.newState = newState;
    }

    public T OldState
    {
        get { return this.oldState; }
    }

    public T NewState
    {
        get { return this.newState; }
    }
}

public delegate Task CreateTaskForTransitionCallback(CancellationToken cancellationToken, object state);

public delegate bool ValidateTransitionCallback<T>(T currentState)
    where T : struct;

public class StateMachineTaskFactory<T> : TaskFactory
    where T : struct
{
    private static readonly ExceptionHelper exceptionHelper = new ExceptionHelper(typeof(StateMachineTaskFactory<>));
    private readonly ConcurrentDictionary<T, TransitionRegistrationInfo> transitionRegistrations;
    private readonly object stateSync;

    // the current state
    private T state;

    // the state to which we're currently transitioning
    private T? transitionToState;

    // the task performing the transition
    private Task transitionToTask;

    public StateMachineTaskFactory()
        : this(default(T))
    {
    }

    public StateMachineTaskFactory(T startState)
    {
        this.transitionRegistrations = new ConcurrentDictionary<T, TransitionRegistrationInfo>();
        this.stateSync = new object();
        this.state = startState;
    }

    public event EventHandler<StateChangedEventArgs<T>> StateChanged;

    public T State
    {
        get
        {
            return this.state;
        }

        private set
        {
            if (!EqualityComparer<T>.Default.Equals(this.state, value))
            {
                var oldState = this.state;
                this.state = value;
                this.OnStateChanged(new StateChangedEventArgs<T>(oldState, value));
            }
        }
    }

    public void RegisterTransition(T beginTransitionState, T endTransitionState, CreateTaskForTransitionCallback createTaskCallback)
    {
        createTaskCallback.AssertNotNull("factory");

        var transitionRegistrationInfo = new TransitionRegistrationInfo(beginTransitionState, createTaskCallback);
        var registered = this.transitionRegistrations.TryAdd(endTransitionState, transitionRegistrationInfo);
        exceptionHelper.ResolveAndThrowIf(!registered, "transitionAlreadyRegistered", endTransitionState);
    }

    public Task TransitionTo(T endTransitionState, CancellationToken cancellationToken = default(CancellationToken), ValidateTransitionCallback<T> validateTransitionCallback = null, object state = null)
    {
        lock (this.stateSync)
        {
            if (EqualityComparer<T>.Default.Equals(this.state, endTransitionState))
            {
                // already in the requested state - nothing to do
                return TaskUtil.FromResult(true);
            }
            else if (this.transitionToState.HasValue && EqualityComparer<T>.Default.Equals(this.transitionToState.Value, endTransitionState))
            {
                // already in the process of transitioning to the requested state - return same transition task
                return this.transitionToTask;
            }
            else if (this.transitionToTask != null)
            {
                // not in the requested state, but there is an outstanding transition in progress, so come back to this request once it's done
                return this.transitionToTask.Then(x => this.TransitionTo(endTransitionState, cancellationToken, validateTransitionCallback, state));
            }
            else if (validateTransitionCallback != null && !validateTransitionCallback(this.State))
            {
                // transition is forbidden, so return a failing task to that affect
                var taskCompletionSource = new TaskCompletionSource<bool>();
                var exception = new StateTransitionForbiddenException<T>(endTransitionState, this.State);
                taskCompletionSource.TrySetException(exception);
                return taskCompletionSource.Task;
            }

            // else, need to transition to the chosen state
            TransitionRegistrationInfo transitionRegistrationInfo;
            var result = this.transitionRegistrations.TryGetValue(endTransitionState, out transitionRegistrationInfo);
            exceptionHelper.ResolveAndThrowIf(!result, "transitionNotRegistered", endTransitionState);

            var beginTransitionState = transitionRegistrationInfo.BeginTransitionState;
            var task = transitionRegistrationInfo.TaskFactory(cancellationToken, state);
            exceptionHelper.ResolveAndThrowIf(task == null, "taskFactoryReturnedNull", endTransitionState);

            var previousState = this.State;
            this.State = beginTransitionState;
            this.transitionToState = endTransitionState;
            this.transitionToTask = task
                .ContinueWith(
                    x =>
                        {
                            if (x.IsFaulted || cancellationToken.IsCancellationRequested)
                            {
                                // faulted or canceled, so roll back to previous state
                                lock (this.stateSync)
                                {
                                    this.State = previousState;
                                    this.transitionToState = null;
                                    this.transitionToTask = null;
                                }

                                if (x.IsFaulted)
                                {
                                    throw x.Exception;
                                }

                                cancellationToken.ThrowIfCancellationRequested();
                            }
                            else
                            {
                                // succeeded, so commit to end state
                                lock (this.stateSync)
                                {
                                    this.State = endTransitionState;
                                    this.transitionToState = null;
                                    this.transitionToTask = null;
                                }
                            }
                        });

            return this.transitionToTask;
        }
    }

    protected virtual void OnStateChanged(StateChangedEventArgs<T> e)
    {
        this.StateChanged.Raise(this, e);
    }

    private struct TransitionRegistrationInfo
    {
        private readonly T beginTransitionState;
        private readonly CreateTaskForTransitionCallback taskFactory;

        public TransitionRegistrationInfo(T beginTransitionState, CreateTaskForTransitionCallback taskFactory)
        {
            this.beginTransitionState = beginTransitionState;
            this.taskFactory = taskFactory;
        }

        public T BeginTransitionState
        {
            get { return this.beginTransitionState; }
        }

        public CreateTaskForTransitionCallback TaskFactory
        {
            get { return this.taskFactory; }
        }
    }
}

而且,为了完整起见,我的单元测试:

public sealed class StateMachineTaskFactoryFixture
{
    #region Supporting Enums

    private enum State
    {
        Undefined,
        Starting,
        Started,
        Stopping,
        Stopped
    }

    #endregion

    [Fact]
    public void default_ctor_uses_default_value_for_start_state()
    {
        var factory = new StateMachineTaskFactory<State>();
        Assert.Equal(State.Undefined, factory.State);
    }

    [Fact]
    public void ctor_can_set_start_state()
    {
        var factory = new StateMachineTaskFactory<State>(State.Stopped);
        Assert.Equal(State.Stopped, factory.State);
    }

    [Fact]
    public void register_transition_throws_if_factory_is_null()
    {
        var factory = new StateMachineTaskFactory<State>();
        Assert.Throws<ArgumentNullException>(() => factory.RegisterTransition(State.Starting, State.Started, null));
    }

    [Fact]
    public void register_transition_throws_if_transition_already_registered()
    {
        var factory = new StateMachineTaskFactory<State>();
        factory.RegisterTransition(State.Starting, State.Started, (ct, o) => TaskUtil.FromResult(true));

        var ex = Assert.Throws<InvalidOperationException>(() => factory.RegisterTransition(State.Starting, State.Started, (ct, o) => TaskUtil.FromResult(true)));
        Assert.Equal("A transition to state 'Started' has already been registered.", ex.Message);
    }

    [Fact]
    public void transition_to_throws_if_no_transition_registered_for_state()
    {
        var factory = new StateMachineTaskFactory<State>();

        var ex = Assert.Throws<InvalidOperationException>(() => factory.TransitionTo(State.Started));
        Assert.Equal("No transition to state 'Started' has been registered.", ex.Message);
    }

    [Fact]
    public void transition_to_throws_if_task_factory_returns_null()
    {
        var factory = new StateMachineTaskFactory<State>();
        factory.RegisterTransition(State.Starting, State.Started, (ct, o) => null);

        var ex = Assert.Throws<InvalidOperationException>(() => factory.TransitionTo(State.Started));
        Assert.Equal("Task factory for end state 'Started' returned null.", ex.Message);
    }

    [Fact]
    public void transition_to_returns_same_task_if_called_multiple_times_whilst_initial_task_is_still_in_progress()
    {
        var factory = new StateMachineTaskFactory<State>();
        factory.RegisterTransition(State.Starting, State.Started, (ct, o) => TaskUtil.Delay(TimeSpan.FromMilliseconds(250)));

        var initialTask = factory.TransitionTo(State.Started);

        Assert.Equal(initialTask, factory.TransitionTo(State.Started));
        Assert.Equal(initialTask, factory.TransitionTo(State.Started));
        Assert.Equal(initialTask, factory.TransitionTo(State.Started));

        Assert.True(initialTask.Wait(TimeSpan.FromSeconds(3)));
    }

    [Fact]
    public void transition_to_returns_completed_task_if_already_in_desired_state()
    {
        var factory = new StateMachineTaskFactory<State>();
        factory.RegisterTransition(State.Starting, State.Started, (ct, o) => TaskUtil.FromResult(true));

        factory.TransitionTo(State.Started).Wait();

        Assert.Equal(TaskStatus.RanToCompletion, factory.TransitionTo(State.Started).Status);
    }

    [Fact]
    public void transition_to_passes_any_state_to_task_creation_function()
    {
        var factory = new StateMachineTaskFactory<State>();
        string receivedState = null;
        factory.RegisterTransition(
            State.Starting,
            State.Started,
            (ct, o) =>
            {
                receivedState = o as string;
                return TaskUtil.FromResult(true);
            });

        factory.TransitionTo(State.Started, CancellationToken.None, null, "here is the state").Wait();

        Assert.Equal("here is the state", receivedState);
    }

    [Fact]
    [SuppressMessage("Microsoft.Naming", "CA2204", Justification = "It's not a word - it's a format string!")]
    public void transition_to_ensures_previous_transition_is_first_completed_before_starting_subsequent_transition()
    {
        var factory = new StateMachineTaskFactory<State>();
        factory.RegisterTransition(State.Starting, State.Started, (ct, o) => TaskUtil.Delay(TimeSpan.FromMilliseconds(10)));
        factory.RegisterTransition(State.Stopping, State.Stopped, (ct, o) => TaskUtil.Delay(TimeSpan.FromMilliseconds(10)));

        var startedAt = DateTime.MinValue;
        var stoppedAt = DateTime.MinValue;
        var startedTask = factory.TransitionTo(State.Started).ContinueWith(x => startedAt = DateTime.UtcNow, TaskContinuationOptions.ExecuteSynchronously);
        var stoppedTask = factory.TransitionTo(State.Stopped).ContinueWith(x => stoppedAt = DateTime.UtcNow, TaskContinuationOptions.ExecuteSynchronously);

        Assert.True(Task.WaitAll(new Task[] { startedTask, stoppedTask }, TimeSpan.FromSeconds(3)), "Timed out waiting for tasks to complete.");
        Assert.True(stoppedAt > startedAt, "stoppedAt is " + stoppedAt.Millisecond + " and startedAt is " + startedAt.Millisecond + ", difference is " + (stoppedAt - startedAt).ToString());
    }

    [Fact]
    public void transition_to_can_be_canceled_before_transition_takes_place()
    {
        var factory = new StateMachineTaskFactory<State>();
        var cancellationTokenSource = new CancellationTokenSource();
        factory.RegisterTransition(State.Starting, State.Started, (ct, o) => TaskUtil.FromResult(true));

        cancellationTokenSource.Cancel();
        var startedTask = factory.TransitionTo(State.Started, cancellationTokenSource.Token);

        try
        {
            startedTask.Wait();
            Assert.True(false, "Failed to throw exception.");
        }
        catch (AggregateException ex)
        {
            Assert.Equal(1, ex.InnerExceptions.Count);
            Assert.IsType<OperationCanceledException>(ex.InnerExceptions[0]);
        }
    }

    [Fact]
    public void transition_to_can_be_canceled()
    {
        var factory = new StateMachineTaskFactory<State>();
        var cancellationTokenSource = new CancellationTokenSource();
        factory.RegisterTransition(State.Starting, State.Started, (ct, o) => TaskUtil.FromResult(true));
        factory.RegisterTransition(State.Stopping, State.Stopped, (ct, o) => TaskUtil.Delay(TimeSpan.FromMilliseconds(150)));

        var startedTask = factory.TransitionTo(State.Started, cancellationTokenSource.Token);
        startedTask.ContinueWith(x => cancellationTokenSource.Cancel());
        var stoppedTask = factory.TransitionTo(State.Stopped, cancellationTokenSource.Token);

        startedTask.Wait(TimeSpan.FromSeconds(3));

        try
        {
            stoppedTask.Wait(TimeSpan.FromSeconds(3));
            Assert.True(false, "Failed to throw exception.");
        }
        catch (AggregateException ex)
        {
            Assert.Equal(1, ex.InnerExceptions.Count);
            Assert.IsType<OperationCanceledException>(ex.InnerExceptions[0]);
        }
    }

    [Fact]
    public void transition_to_can_be_forbidden()
    {
        var factory = new StateMachineTaskFactory<State>();
        factory.RegisterTransition(State.Starting, State.Started, (ct, o) => TaskUtil.FromResult(true));
        factory.RegisterTransition(State.Stopping, State.Stopped, (ct, o) => TaskUtil.FromResult(true));

        var startedTask = factory.TransitionTo(State.Started, CancellationToken.None, x => x == State.Undefined);
        var stoppedTask = factory.TransitionTo(State.Stopped, CancellationToken.None, x => x != State.Started);

        startedTask.Wait(TimeSpan.FromSeconds(3));

        try
        {
            stoppedTask.Wait(TimeSpan.FromSeconds(3));
            Assert.True(false, "Failed to throw exception.");
        }
        catch (AggregateException ex)
        {
            Assert.Equal(1, ex.InnerExceptions.Count);
            var ex2 = Assert.IsType<StateTransitionForbiddenException<State>>(ex.InnerExceptions[0]);
            Assert.Equal(State.Stopped, ex2.TargetState);
            Assert.Equal(State.Started, ex2.State);
            Assert.Equal("A transition to state 'Stopped' was forbidden by the validate transition callback.", ex2.Message);
        }
    }

    [Fact]
    public void canceled_transition_reverts_back_to_original_state()
    {
        var factory = new StateMachineTaskFactory<State>();
        var cancellationTokenSource = new CancellationTokenSource();
        factory.RegisterTransition(State.Starting, State.Started, (ct, o) => TaskUtil.FromResult(true));
        factory.RegisterTransition(State.Stopping, State.Stopped, (ct, o) => TaskUtil.Delay(TimeSpan.FromSeconds(3), cancellationTokenSource.Token));

        factory.StateChanged += (s, e) =>
            {
                if (e.NewState == State.Stopping)
                {
                    // cancel the stop
                    cancellationTokenSource.Cancel();
                }
            };

        var startedTask = factory.TransitionTo(State.Started);
        var stoppedTask = factory.TransitionTo(State.Stopped, cancellationTokenSource.Token);

        startedTask.Wait(TimeSpan.FromSeconds(3));

        try
        {
            stoppedTask.Wait(TimeSpan.FromSeconds(3));
            Assert.True(false, "Failed to throw exception.");
        }
        catch (AggregateException ex)
        {
            Assert.Equal(1, ex.InnerExceptions.Count);
            Assert.IsType<OperationCanceledException>(ex.InnerExceptions[0]);
            Assert.Equal(State.Started, factory.State);
        }
    }

    [Fact]
    public void failed_transition_reverts_back_to_original_state()
    {
        var factory = new StateMachineTaskFactory<State>();
        factory.RegisterTransition(State.Starting, State.Started, (ct, o) => TaskUtil.FromResult(true));
        factory.RegisterTransition(State.Stopping, State.Stopped, (ct, o) => { throw new InvalidOperationException("Something went wrong"); });

        var startedTask = factory.TransitionTo(State.Started);
        var stoppedTask = factory.TransitionTo(State.Stopped);

        startedTask.Wait(TimeSpan.FromSeconds(3));

        try
        {
            stoppedTask.Wait(TimeSpan.FromSeconds(3));
            Assert.True(false, "Failed to throw exception.");
        }
        catch (AggregateException ex)
        {
            Assert.Equal(1, ex.InnerExceptions.Count);
            Assert.IsType<InvalidOperationException>(ex.InnerExceptions[0]);
            Assert.Equal(State.Started, factory.State);
        }
    }

    [Fact]
    public void state_change_is_raised_as_state_changes()
    {
        var factory = new StateMachineTaskFactory<State>(State.Stopped);
        factory.RegisterTransition(State.Starting, State.Started, (ct, o) => TaskUtil.FromResult(true));
        factory.RegisterTransition(State.Stopping, State.Stopped, (ct, o) => TaskUtil.FromResult(true));
        var stateChanges = new List<StateChangedEventArgs<State>>();
        factory.StateChanged += (s, e) => stateChanges.Add(e);

        factory.TransitionTo(State.Started).Wait(TimeSpan.FromSeconds(1));
        factory.TransitionTo(State.Stopped).Wait(TimeSpan.FromSeconds(1));
        factory.TransitionTo(State.Started).Wait(TimeSpan.FromSeconds(1));
        factory.TransitionTo(State.Stopped).Wait(TimeSpan.FromSeconds(1));

        Assert.Equal(8, stateChanges.Count);
        Assert.Equal(State.Stopped, stateChanges[0].OldState);
        Assert.Equal(State.Starting, stateChanges[0].NewState);
        Assert.Equal(State.Starting, stateChanges[1].OldState);
        Assert.Equal(State.Started, stateChanges[1].NewState);
        Assert.Equal(State.Started, stateChanges[2].OldState);
        Assert.Equal(State.Stopping, stateChanges[2].NewState);
        Assert.Equal(State.Stopping, stateChanges[3].OldState);
        Assert.Equal(State.Stopped, stateChanges[3].NewState);
        Assert.Equal(State.Stopped, stateChanges[4].OldState);
        Assert.Equal(State.Starting, stateChanges[4].NewState);
        Assert.Equal(State.Starting, stateChanges[5].OldState);
        Assert.Equal(State.Started, stateChanges[5].NewState);
        Assert.Equal(State.Started, stateChanges[6].OldState);
        Assert.Equal(State.Stopping, stateChanges[6].NewState);
        Assert.Equal(State.Stopping, stateChanges[7].OldState);
        Assert.Equal(State.Stopped, stateChanges[7].NewState);
    }

    [Fact]
    public void state_gets_the_current_state()
    {
        var factory = new StateMachineTaskFactory<State>(State.Stopped);
        factory.RegisterTransition(State.Starting, State.Started, (ct, o) => TaskUtil.Delay(TimeSpan.FromMilliseconds(100)));
        factory.RegisterTransition(State.Stopping, State.Stopped, (ct, o) => TaskUtil.Delay(TimeSpan.FromMilliseconds(100)));

        var task = factory.TransitionTo(State.Started);
        Assert.Equal(State.Starting, factory.State);
        task.Wait(TimeSpan.FromSeconds(3));
        Assert.Equal(State.Started, factory.State);
        task = factory.TransitionTo(State.Stopped);
        Assert.Equal(State.Stopping, factory.State);
        task.Wait(TimeSpan.FromSeconds(3));
        Assert.Equal(State.Stopped, factory.State);
    }
}
于 2012-11-14T10:45:51.837 回答
0

可能不是您想要的,但是如果将它们排队而不是忽略是意图(或者是可以接受的),那么一个相对简单的选择是对这 2 个 StartNew 调用使用有限并发调度程序。

获得您正在寻找的效果所需的代码比需要的代码多,但可以让您利用已经编写的东西而不是编写自己的代码。:)

于 2012-07-24T16:29:25.807 回答
-1

好吧,如果您一次只希望类的一个实例处于活动状态,您应该考虑使用单例模式。

我不是模式专家,所以如果我使用的解决方案总是有名字,我不知道。我不使用任务而是线程。这是我的代码。

public class Service
{
    private object oLock;
    private bool running;
    private Thread threadTask;
    private AutoResetEvent taskStarted;

    public bool IsRunning { get { return this.running; } }

    public Service()
    {
        oLock = new object();
        taskStarted = new AutoResetEvent(false);
        running = false;
    }

    public void Start()
    {
        // If we can not acquire the lock, then there is a Start or Stop operation 
        // in progress, so better leave. Also leave if already running.
        if (running || !Monitor.TryEnter(oLock))
            return;

        // Set running flag to prevent other threads to start service again
        running = true;

        // Create and start the thread
        threadTask = new Thread(new ThreadStart(Task));
        threadTask.IsBackground = true;
        threadTask.Start();

        // Wait until the task execution begins. This is optional
        taskStarted.WaitOne();

        // Release the lock
        Monitor.PulseAll(oLock);
        Monitor.Exit(oLock);
    }

    public void Stop()
    {
        // If we can not acquire the lock, then there is a Start or Stop operation 
        // in progress, so better leave. Also leave if not running.
        if(!running || !Monitor.TryEnter(oLock))
            return;

        // Clear the running task to prevent reentrancy
        running = false;

        // Here we can abort the thread. This is optional an depends on task
        threadTask.Abort();

        // Wait until the thrad finish
        threadTask.Join();

        // Clear
        threadTask = null;

        // Release the lock
        Monitor.PulseAll(oLock);
        Monitor.Exit(oLock);
    }

    protected virtual void Task()
    {
        // Setup task and allocate resources
        taskStarted.Set();

        // Execute the task poll
        while (running)
        {
        }

        // Finish the task and release resources
    }
}

其中一个技巧是使用 Monitor 类而不是 lock 语句,因此尝试启动服务的第一个线程将获胜,而其他线程将获得已经启动或停止的服务。如果您打算颠倒顺序,请小心

if (Monitor.TryEnter() || running)
    return;

您必须调用 Monitor.Exit 否则您的应用程序将死锁。

当然,您可以更改表示空闲、启动、运行和停止状态的枚举的运行布尔值。

使类抽象并让继承者覆盖要在/而不是while语句中调用的方法也是一个好主意。

希望这可以帮助。此致。

于 2012-08-08T20:36:27.367 回答