【问题标题】:How to wait if the first code is under execution?如果第一个代码正在执行中,如何等待?
【发布时间】:2017-02-23 12:05:09
【问题描述】:

我有一种情况,当用户单击一个 tabitem 时,用户创建了一个类的实例。这个类包含一个绘制图表的函数,它非常耗时,所以它是异步编写的。

现在的问题是假设用户第一次单击选项卡并且类实例化并且长时间的过程工作了很长时间,同时之前的异步任务没有完成并且用户再次单击同一个选项卡。

在这种情况下,我必须等到前一个异步任务没有完成,然后在第二次点击时,tabitem 必须在第一个异步任务完成后创建实例(它应该等到第一个异步进程没有完成)。

代码在这里:

if (selectedTabIndex == 2) //What is the user selected second time whil the previous task is still not finshed ? 
{         
    DrawGraph obj= new DrawGraph(selectedItem.Name);
 }   

我在 DrawGraph 类构造函数的某个地方完成了:

 public DrawGraph(string Name)
 {
   timeConsumingProcess(Name);
 }

 public async void timeConsumingProcess(string Name)
 {       
   await  startTaimeConsumingProcess();
 }

我想要的是当用户第二次单击此选项卡项目编号 = 2 时,它必须等到上一个异步任务完成,然后它必须再次实例化 DrawGraph 类以 再次重新启动异步。

如何实现?

【问题讨论】:

  • 为什么不禁用标签?
  • @HristoYankov 是的,我有这个想法,但我的前辈不接受。

标签: c# .net multithreading asynchronous async-await


【解决方案1】:

在这种情况下,我必须等到前一个异步任务未完成,然后在第二次点击时,tabitem 必须在第一个异步任务完成后创建实例

然后让您的代码 (a) 等待最后一个任务:

private Task drawGraph = null;
...
if (selectedTabIndex == 2)
{
  if (drawGraph != null)
    await drawGraph;
  DrawGraph obj = new DrawGraph(selectedItem.Name);
  drawGraph = obj.timeConsumingProcess();
}

...

private readonly string name;
public DrawGraph(string Name)
{
  name = Name;
}

public async Task timeConsumingProcess()
{
  await  startTaimeConsumingProcess();
}

请注意,这要求您使用async Task 而不是async void,这很好,因为您应该使用avoid async void

【讨论】:

    【解决方案2】:

    您可以将代表长时间运行操作的任务存储在表单中的实例变量中,并使用它来检查任务是否仍在运行。

    private Task drawGraphTask = null;
    
    private void button1_Click(object sender, EventArgs e)
    {
        DrawGraph();
    }
    
    private async void DrawGraph()
    {
        // Only perform task when this is the first time 
        // or the previous task is already completed
        if (drawGraphTask == null || drawGraphTask.IsCompleted)
        {
            drawGraphTask = startTimeConsumingProcess();
            await drawGraphTask;
        }
        else
        {
            MessageBox.Show("Task already active");
        }
    }
    
    private Task startTimeConsumingProcess()
    {
        // Your Code here
        return Task.Delay(5000);
    }
    

    从构造函数内部启动异步活动是不好的。将异步逻辑移到 DrawGraph 类之外,使类的实例化已经在表单级别异步。

    【讨论】:

    • 条件 drawGraphTask == null 甚至会在第二次点击时执行,因为第二次点击后调试的参数是这些“Id = 744, Status = RanToCompletion, Method = "{null}"" 但它仍然执行这个条件
    • 不,条件检查整个Task是否为null,不管task的Method属性有什么值。
    【解决方案3】:

    如果这是一个非常耗时的过程,您可以选择在不同的线程中进行

    public DrawGraph(string Name)
    {
        var task = new Task(() => timeConsumingProcess(Name));
        task.Start();
    }
    

    这样你的主线程就不会被阻塞。如果您想在这个长时间运行的任务完成后运行一些代码,请使用 ContinueWith 方法。

    public DrawGraph(string Name)
    {
        _canPressTab = false;
        var task = new Task(() => timeConsumingProcess(Name));
        task.Start();
        task.ContinueWith(t => {
            _canPressTab = true;
            ...
        });
    }
    

    下面更新

    如此处所示,您可以在单击选项卡时调用 Draw 方法。这将检查任务是否已在运行。如果没有,它会启动任务。如果任务正在运行,它将等待它完成,然后开始一个新任务。

        public Task task = new Task(DoSomething);
        private static void DoSomething()
        {
            Thread.Sleep(10000);
        }
    
    
        private void Draw()
        {
            //if tab is clicked run the Draw method
            if (task.Status.Equals(TaskStatus.Running))
            {
                task.Wait();
                task = new Task(DoSomething);
                task.Start();
            }
            else if (task.Status.Equals(TaskStatus.RanToCompletion))
            {
                task = new Task(DoSomething);
                task.Start();
            }
            else
            {
                task.Start();
            }
        }
    

    【讨论】:

    • 我需要做的是等待 if (selectedTabIndex == 2) 条件,而 previosu 任务正在处理。如果处理则让它完成然后处理下一个实例。怎么办?
    【解决方案4】:

    一个简单的改变就可以实现这一点

    public async Task DrawGraph(string Name)
    {
        await timeConsumingProcess(Name);
    }
    
    public async Task timeConsumingProcess(string Name)
    {       
        await startTaimeConsumingProcess();
    }
    

    我还让DrawGraph() 返回了Task,以防您需要等待它。

    【讨论】:

    • 使用 await 不会阻止处理用户操作,所以问题仍然存在。
    • @NineBerry 你是对的。但这不是用户要求的。即使已按下按钮,用户仍要求等待任务。因此,对DrawGraph() 的下一次调用将等待上一次调用完成。
    猜你喜欢
    • 2020-12-12
    • 1970-01-01
    • 2017-08-12
    • 1970-01-01
    • 2015-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-21
    相关资源
    最近更新 更多