如何确保只运行一个Task实例?

时间:2017-08-18 13:57:16

标签: c# async-await

我有一个长期运行的功能"需要在计时器上触发,我也希望能够通过按钮点击触发。任务正在运行时,按钮将切换为“取消”按钮。按钮。我只希望一次运行一个任务的单个实例。我已将任务设为私有变量,以便计时器可以检查任务的IsCompleted状态。为此,我创建了一个私有变量

private Task sync;

然后我有一个执行任务的功能

private async Task syncUsers()
{
    //
}

现在,点击按钮,我将变量分配给函数

sync = syncUsers();
await sync;

似乎任务完成时就是这样。如何重新启动任务/功能?我尝试使用任务工厂和异步委托,但没有工作。

sync = Task.Factory.StartNew(async delegate { await syncStudents(); });
await sync;

我有一种感觉,我完全错了但不知道从哪里开始。

3 个答案:

答案 0 :(得分:0)

你无法重新开始。您必须创建新任务。如果您想支持多个并行任务执行而不是Task sync,请执行List<Task> syncTasks,然后执行syncTasks.Add(syncUsers())。然后,您可以在awaitTask.WhenAll(syncTasks)

除非我误解了这个问题

修改 要定期运行同步任务,请执行以下操作:

private System.Threading.Timer _syncTimer;
private bool _isRunning;
private int refreshInterval = 30;

public YourClass(){
    _syncTimer = new System.Threading.Timer(TimerCallback);
}

public void StartSyncTimer(){
    _syncTimer.Change(0, 0);
}

private async Task SyncUsers(){
        //
}
private void TimerCallback(object userState){
    _isRunning = true;
    var task = SyncUsers();
    task.ContinueWith(t => {
        _isRunning = false;
        _syncTimer.Change(TimeSpan.FromSeconds(refreshInterval), TimeSpan.FromSeconds(0));
    });
}

然后在您的Click处理程序中,您可以执行以下操作:

if(!_isRunning){
    _syncTimer.Change(0,0);
}

说明:在构造函数中,我们初始化一个定期执行TimerCallback方法的System.Threading.Timer实例。

然后我们在类中添加一个公共方法来立即启动定时器执行:_syncTimer.Change(0,0)。这里第一个零表示第一次执行(立即)之前的延迟,第二个零表示重复间隔。它也是零意味着它不会自动重复。

然而,在继续执行SyncUsers任务时,我们会将计时器重新安排到您想要的任何时间段。

在计时器回调中,我们还将_IsRunning设置为true以提醒任何感兴趣的各方状态,然后在任务完成时我们将其设置为true。

上面代码的行为是:如果同步任务是由计时器启动的,那么单击将不会执行任何操作,因为_isRunning将为true;如果同步是在完成refreshIntervals之后完成的,则点击将立即执行同步,然后完成将重新安排同步以供以后使用。

免责声明:我实际上并没有运行上面的代码,但这是我已经完成了数百次的工作,可以让我们付出更多的努力。

答案 1 :(得分:0)

private Task sync;
private readonly object syncLock = new object();
private Task syncUsers()
{
    lock (syncLock)
    {
        if (this.sync == null || this.sync.IsCompleted)
        {
            this.sync = ...
        }
    }

    return this.sync;
}

答案 2 :(得分:-1)

让我们假设您在创建任务时执行类似

的操作
Task task = new Task(()=>{ DoStuff(); });

您需要做的是,不保留任务以便以后执行。您需要保留委托,并在以后使用相同的委托创建另一个任务。

如果你不确定如何,查看Action<T>,它可能会给你一些想法。