异步执行I命令

时间:2017-12-28 10:11:34

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

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

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

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

1 个答案:

答案 0 :(得分:3)

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

  

等待完成一项或多项任务

     

由于任务通常在线程池线程上异步运行,因此创建并启动任务的线程会在实例化任务后立即继续执行。在某些情况下,当调用线程是主应用程序线程时,应用程序可能会在任何实际开始执行之前终止。在其他情况下,应用程序的逻辑可能要求调用线程仅在一个或多个任务完成执行时继续执行。您可以通过调用Wait方法来等待一个或多个任务完成,从而同步调用线程的执行和它启动的异步任务。

我希望这会有所帮助。

修改
您最好使用Task.Wait()而不是Thread.Sleep(),因为通常您不知道线程何时完成:

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

这不起作用,因为您在RunTest()中启动了一个新线程。然后,在Print()中创建的线程返回并取消阻塞返回并终止每个线程的主线程。您可以通过在Thread.Sleep()中同步运行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();
    }
}