2017-12-28 1 views
1

Der Einfachheit halber habe ich meinen Xamarin nUnit-Testfehler als Konsolenanwendung reproduziert und zeigt das gleiche Problem, das ich nicht verstehen kann. Also zuerst der Code, der funktioniert und zweitens der Code, der nicht funktioniert.I-Befehl asynchron ausführen

Einfache Konsole app

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

Dies funktioniert gut, die Konsole druckt läuft ... dann druckt Test in einer Sekunde. Nun ist die zweite Asynchron-Version, die nur Druck läuft ...

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

So ist der zweite Fall führt nur einen Teil des Codes, wenn es bekommt Task.Run (() => Thread.Sleep (1000)) zu erwarten ; Es bleibt nur die Methode, nie wieder zu kommen. Ich verstehe nicht warum und wie man das löst. Hat jemals jemand das gleiche Problem gefunden? Vielen Dank.

+2

Mögliche Duplikat [Kann nicht geben Sie den ‚async‘ Modifikator auf der ‚Main‘ Methode einer Konsole app] (https://stackoverflow.com/questions/9208921/cant -specify-the-async-Modifikator-on-the-main-Methode-einer-Konsole-App) – mjwills

Antwort

3

Der Haupt-Thread wird beendet, bevor Thread.Sleep(1000); fertig ist und alle untergeordneten Threads. Sie können versuchen, am Ende Ihrer Main-Methode eine Thread.Sleep(2000); hinzuzufügen oder etwas anderes machen lassen. Es sollte dann funktionieren. Auch einen Blick auf Microsoft's Task class documentation:

für eine oder mehr Aufgaben Warten

abzuschließen Da Aufgaben typischerweise asynchron auf einem Thread-Pool Thread ausgeführt, den Thread, erstellt und startet die Ausführung die Aufgabe weiterhin so schnell da die Aufgabe instanziiert wurde. In einigen Fällen, wenn der aufrufende Thread der Hauptanwendungs-Thread ist, wird die Anwendung möglicherweise beendet, bevor eine Task tatsächlich mit der Ausführung beginnt. In anderen Fällen erfordert die Logik Ihrer Anwendung, dass der aufrufende Thread die Ausführung nur dann fortsetzt, wenn eine oder mehrere Tasks die Ausführung abgeschlossen haben. Sie können die Ausführung des aufrufenden Threads und die von ihm gestarteten asynchronen Tasks synchronisieren, indem Sie eine Wait-Methode aufrufen, um auf den Abschluss einer oder mehrerer Tasks zu warten.

Ich hoffe, das hilft.

Edit:
Sie sollten besser nutzen Task.Wait() anstelle von Thread.Sleep(), weil oft Sie wissen nicht, wann ein Thread beenden wird:

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

Dies gilt nicht funktionieren, weil Sie in RunTest() einen neuen Thread starten. Dann gibt der in Print() erstellte Thread den Haupt-Thread zurück und entsperrt ihn, der jeden Thread zurückgibt und beendet. Sie könnten dies lösen, indem Sie Thread.Sleep() in RunTest() synchron ausführen. Alles würde wie folgt aussehen:

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

Ja !!!! Sobald ich Thread.Sleep auf den Hauptthread gesetzt hatte, hatte es etwas, zu dem ich zurückkehren konnte, damit es nicht aus dem Main sprang. –

Verwandte Themen