异步执行 I 命令

Executing I command asynchronously

为了简单起见,我将我的 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();
    }
}

这工作正常,控制台打印 运行... 然后打印测试在一秒钟内完成。现在第二个异步版本只打印 运行...

 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() returns 中创建线程并取消阻塞 returns 的主线程并终止每个线程。您可以通过 运行 Thread.Sleep() in RunTest() 同步解决此问题。一切看起来像这样:

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();
    }
}