异步方法并不总是在Windows Phone 8应用程序上执行

时间:2014-12-23 09:14:37

标签: c# windows-phone-8 async-await

我正在开发一款需要在一台Windows Phone 8设备上运行的应用。应用程序需要连接到多个URL并记录每个连接尝试的结果:响应的大小和整个请求 - 响应所花费的时间。它主要起作用,但我不能让异步方法发挥得很好:并非所有尝试都被记录下来。通常不会记录最后一次测试,这对我来说看起来好像没有调用相关方法。

以下是我的MainViewModel中的相关代码:

private readonly BackgroundWorker backgroundWorker;

public MainViewModel()
{
    backgroundWorker = new BackgroundWorker { WorkerReportsProgress = true, WorkerSupportsCancellation = false };
    backgroundWorker.DoWork += BackgroundWorkerDoWork;
    backgroundWorker.ProgressChanged += BackgroundWorkerProgressChanged;
    backgroundWorker.RunWorkerCompleted += BackgroundWorkerRunCompleted;
}

private void BackgroundWorkerDoWork(object sender, DoWorkEventArgs args)
{
    var worker = sender as BackgroundWorker;
    if (worker == null) return;

    var testItems = new TestItemListCreator().Execute(NetworkType);
    var total = testItems.Count;

    var count = 0;
    foreach (var testItem in testItems)
    {
        count++;
        // because otherwise the logging fails
        Thread.Sleep(TimeSpan.FromSeconds(2)); 
        new TestExecutor().RunTest(testItem);
        worker.ReportProgress((count * 100) / total);
    }
}

private void BackgroundWorkerProgressChanged(object sender, ProgressChangedEventArgs args)
{
    ProgressPercent = args.ProgressPercentage;
}

private void BackgroundWorkerRunCompleted(object sender, RunWorkerCompletedEventArgs args)
{
    if (args.Error == null)
    {
        Message = "Done!";
    }
    else
    {
        Message = "Error: " + args.Error.Message;
    }
}

private void ExecuteRunTests()
{
    Message = "Please wait...";
    ProgressPercent = 0;

    if (backgroundWorker.IsBusy) return;

    CanExecuteRunTests = false;
    backgroundWorker.RunWorkerAsync();
}

这是TestExecutor:

public class TestExecutor
{
    private readonly OAuthGetRequestExecutor oAuthGetRequestExecutor;

    public TestExecutor()
    {
        oAuthGetRequestExecutor = new OAuthGetRequestExecutor();
    }

    public void RunTest(TestItem testItem)
    {
        oAuthGetRequestExecutor.PerformGetRequest(testItem, RequestFinished);
    }

    private void RequestFinished(object sender, OAuthEventArgs eventArgs)
    {
        if (eventArgs.IsError)
        {
            ErrorLogger.Log(eventArgs.ErrorMessage);
        }
        else
        {
            TestLogger.Log(eventArgs.TestItem);
        }
    }
}

这是TestLogger:

internal class TestLogger
{
    private const string Separator = ";";

    public static async void Log(TestItem testItem)
    {
        await CsvFileAssistant.WriteToFile(string.Join(Separator, GetLine(testItem)) + Environment.NewLine);
    }

    private static IEnumerable<object> GetLine(TestItem testItem)
    {
        return new List<object>
        {
            testItem.DateTime,
            testItem.NetworkCategory,
            testItem.NetworkType,
            testItem.TestName,
            testItem.TestResult.Sent,
            testItem.TestResult.Received,
            testItem.TestResult.Time
        };
    }
}

这是CsvFileAssistant:

internal class CsvFileAssistant
{
    private static readonly StorageFolder DefaultFolder = ApplicationData.Current.LocalFolder;
    private const string FolderName = "Data";
    private const string FileName = "Tests.csv";

    public static async Task WriteToFile(string text)
    {
        var dataFolder = await DefaultFolder.CreateFolderAsync(FolderName, CreationCollisionOption.OpenIfExists);

        var file = await dataFolder.CreateFileAsync(FileName, CreationCollisionOption.OpenIfExists);

        using (var s = await file.OpenStreamForWriteAsync())
        {
            var fileBytes = Encoding.UTF8.GetBytes(text.ToCharArray());
            s.Seek(0, SeekOrigin.End);
            s.Write(fileBytes, 0, fileBytes.Length);
        }
    }
}

我做错了什么?

3 个答案:

答案 0 :(得分:0)

不确定,但我认为您在使用async / await时违反了规则1:避免异步无效。没有办法判断他们是否已被处决。

http://haacked.com/archive/2014/11/11/async-void-methods/

答案 1 :(得分:0)

此:

TestLogger.Log(eventArgs.TestItem);

没有等待,因为您是从同步方法执行此操作。一旦你不使用await,你就只是使用“即发即弃”机制,这无法保证你的方法能够优雅地完成。

您正在将基于事件的异步模式与新的任务异步模式混合。我建议你使用其中一种,但不能一起使用。

除了事件处理程序之外,您不应该使用async void,这是您的情况:

private async void RequestFinished(object sender, OAuthEventArgs eventArgs)
{
    if (eventArgs.IsError)
    {
        ErrorLogger.Log(eventArgs.ErrorMessage);
    }
    else
    {
        await TestLogger.Log(eventArgs.TestItem);
    }
}

答案 2 :(得分:0)

我已经重写了部分项目以避免使用EventHandler并删除BackgroundWorker。

这是MainViewModel:

private readonly IProgress<int> progress;

public MainViewModel()
{
    progress = new Progress<int>(value =>
    {
        ProgressPercent = value;
    });
}

private async void ExecuteRunTests()
{
    ProgressPercent = 0;

    var testItems = new TestItemListCreator().Execute(NetworkType);
    var total = testItems.Count;

    var count = 0;
    foreach (var testItem in testItems)
    {
        count++;

        var result = await new OAuthGetRequestExecutor().PerformGetRequest(testItem);
        await ResultLogger.Log(result);

        var percent = (count * 100) / total;
        progress.Report(percent);
    }
}

ResultLogger检查IsError的{​​{1}}属性是否为真,并使用该属性决定调用OAuthResultErrorLogger。当然,没有TestLogger