c# - 异步执行 I 命令

标签 c# asynchronous async-await icommand

为了简单起见,我将我的 Xamarin nUnit 测试错误重现为控制台应用程序,它显示了我无法理解的相同问题。因此,首先是有效的代码,其次是无效的代码。

简单的控制台应用

public class Working
{

    private MyViewModel _viewModel;

    public Working()
    {
        Console.WriteLine("Start");
        _viewModel = new MyViewModel();
    }

    static void Main(string[] args)
    {
        Working prog = new Working();
        prog.Print();

    }

    public void Print()
    {
        _viewModel.NewSurveyCommand.Execute(null);
    }
}

public class MyViewModel 
{
    public MyViewModel()
    {
        NewSurveyCommand = new MyCommand(RunTest);
    }

    public ICommand NewSurveyCommand { get; private set; }

    private void RunTest()
    {
        Console.WriteLine("Running...");
        Thread.Sleep(1000);
        Console.WriteLine("Test done");
    }
}

public class MyCommand : ICommand
{
    private Action _action;

    public MyCommand(Action action)
    {
        _action = action;
    }

    public event EventHandler CanExecuteChanged;

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        _action.Invoke();
    }
}

这很好用,控制台打印 running... 然后打印 test done in one second。现在是第二个异步版本,它只打印运行...

 public class Program
 {

    private ViewModel _viewModel;

    public Program()
    {
        Console.WriteLine("Start");
        _viewModel = new ViewModel();
    }

    static void Main(string[] args)
    {
        Program prog = new Program();
        prog.Go();

    }

    async void Go()
    {
        await Print();
    }

    public async Task Print()
    {
        await Task.Run( () =>  _viewModel.NewSurveyCommand.Execute(null) );
    }
}

public class ViewModel 
{
    public ViewModel()
    {
        NewSurveyCommand = new Command(async () => await RunTest());
    }

    public ICommand NewSurveyCommand { get; private set; }

    public async Task RunTest()
    {
        Console.WriteLine("Running...");
        await Task.Run( () => Thread.Sleep(1000));
        Console.WriteLine("Test done");
    }
}

public class Command : ICommand
{
    private Action _action;

    public Command(Action action)
    {
        _action = action;
    }

    public event EventHandler CanExecuteChanged;

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        _action.Invoke();
    }
  }
}

所以第二种情况只执行了部分代码,当它到达 await Task.Run( () => Thread.Sleep(1000));它只是让方法永远不会回来。我不明白为什么以及如何解决这个问题。有没有人遇到过同样的问题。谢谢。

最佳答案

主线程在 Thread.Sleep(1000); 完成之前终止,所有子线程也是如此。您可以尝试在 Main 方法的末尾添加 Thread.Sleep(2000); 或让它执行其他操作。它应该工作。也看看 Microsoft's Task class documentation :

Waiting for one or more tasks to complete

Because tasks typically run asynchronously on a thread pool thread, the thread that creates and starts the task continues execution as soon as the task has been instantiated. In some cases, when the calling thread is the main application thread, the app may terminate before any the task actually begins execution. In others, your application's logic may require that the calling thread continue execution only when one or more tasks has completed execution. You can synchronize the execution of the calling thread and the asynchronous tasks it launches by calling a Wait method to wait for one or more tasks to complete.

希望对您有所帮助。

编辑:
您最好使用 Task.Wait() 而不是 Thread.Sleep(),因为您通常不知道线程何时结束:

static void Main(string[] args)
{
    Program prog = new Program();
    Task t = prog.Print();
    t.Wait();
}

这不起作用,因为您在 RunTest() 中启动了一个新线程。然后在 Print() 中创建的线程返回并解除主线程的阻塞,主线程返回并终止每个线程。您可以通过在 RunTest() 中同步运行 Thread.Sleep() 来解决此问题。一切看起来像这样:

public class Program
{

    private ViewModel _viewModel;

    public Program()
    {
        Console.WriteLine("Start");
        _viewModel = new ViewModel();
    }

    static void Main(string[] args)
    {
        Program prog = new Program();
        Task t = prog.Print();
        t.Wait();
    }

    async void Go()
    {
        await Print();
    }

    public async Task Print()
    {
        await Task.Run(() => _viewModel.NewSurveyCommand.Execute(null));
    }
}

public class ViewModel
{
    public ViewModel()
    {
        NewSurveyCommand = new Command(() => RunTest());
    }

    public ICommand NewSurveyCommand { get; private set; }

    public void RunTest()
    {
        Console.WriteLine("Running...");
        Thread.Sleep(1000);
        Console.WriteLine("Test done");
    }
}

public class Command : ICommand
{
    private Action _action;

    public Command(Action action)
    {
        _action = action;
    }

    public event EventHandler CanExecuteChanged;

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        _action.Invoke();
    }
}

关于c# - 异步执行 I 命令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48005520/

相关文章:

c# - 单元测试项目是否可以加载目标应用程序的 app.config 文件?

c# - 如何使用 C# 和 ADODB 查询 Active Directory?

c# - Asp.Net MVC - 空白模型不返回空白数据

c# - 等待具有不同结果的多个任务

database - 如何在每次点击时重新填充 GXT ComboBox?

c# - MVC 复选框分组

javascript - 在函数内部修改变量后,为什么变量未更改? -异步代码引用

c# - 如果等待的任务链上有 'unwrapped'任务,则Task.Result/wait(..)无限期等待,而如果使用 'async/await',则成功完成

c# - 从 Sync 方法调用 Async 方法的最佳做法是什么?

c# - 等待 Parse 中的 query.FindAsync() 不起作用