3

我正在尝试解决我的 UI 被阻止但我不明白为什么的问题。

public Task AddStuff(string myID, List<string> otherIDs)
{
    Action doIt = () =>
    {
        this.theService.AddStuff(myID, otherIDs);
    };

    return Task.Factory.StartNew(doIt, TaskCreationOptions.LongRunning);
}

如果列表很长,调用可能需要 30 秒,并且整个应用程序变得无响应(在 Windows 7 中变成白色)。

有没有不同的方法来做到这一点,所以它不会阻塞用户界面?


编辑

好的,所以有很多关于此的代码,我将尝试保持相关性。我确实意识到回到原始代码,我已经删除了一些可能很重要的东西。我应该使用不同的 TaskSchedulerTaskScheduler.Current吗?

此外,没有阻止任何此代码的等待语句,并且服务不与 UI 交互。

Task.Factory.StartNew(objState =>
    {
        LoadAssets(objState);
    }, state, this.cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);

private void LoadAssets(object objState)
{
    LoadAssetsState laState = (LoadAssetsState)objState;

    List<string> assetIDs = new List<string>();

    for (int i = 0; i < laState.AddedMediaItems.Count; i++)
    {
        if (laState.CancellationToken.IsCancellationRequested)
            return;

        string assetId = this.SelectFilesStep.AssetService.GetAssetId(laState.AddedMediaItems[i], laState.ActiveOrder.OrderID);

        assetIDs.Add(assetId);

    }

    if (laState.CancellationToken.IsCancellationRequested)
        return;

    this.ApiContext.AddAssetToProduct(laState.ActiveOrder.OrderID, laState.ActiveProduct.LineID, assetIDs, laState.Quantity, laState.CancellationToken).ContinueWith(task =>
    {
        if (laState.CancellationToken.IsCancellationRequested)
            return;


        App.ApiContext.GetOrderDetails(laState.ActiveOrder.OrderID, false, laState.CancellationToken).ContinueWith(orderDetailsTask =>
        {
            if (laState.CancellationToken.IsCancellationRequested)
                return;

            this.activeOrder = orderDetailsTask.Result;

            this.StandardPrintProductsStep.Synchronize(this.activeOrder);

        });
    });
}

public Task AddAssetToProduct(string orderID, string lineID, List<string> assetIDs, int quantity, CancellationToken? cancellationToken = null)
{
    Action doIt = () =>
    {
        if (cancellationToken.IsCancellationRequested())
            return;

        this.ordersService.AddAssetToProduct(orderID, lineID, assetIDs, quantity);
    };

    if (cancellationToken != null)
        return Task.Factory.StartNew(doIt, cancellationToken.Value, TaskCreationOptions.LongRunning, TaskScheduler.Current);
    else
        return Task.Factory.StartNew(doIt, TaskCreationOptions.LongRunning);
}

编辑

我在服务调用之前和之后放置了断点,它是阻塞 UI 的服务调用,而不是任何其他行。

听起来没有理由阻止它,所以我想如果它很长,我会分解列表并进行多次调用。我只是想确保我的任务逻辑没有遗漏任何东西。

4

2 回答 2

6

有没有不同的方法来做到这一点,所以它不会阻塞用户界面?

此调用本身不应阻塞 UI。但是,如果theService.AddStuff与 UI 的 SynchronizationContext 进行一些同步,这可能会导致 UI 被该调用有效地阻止。

否则,问题可能发生在此函数之外。例如,如果您调用Wait()从此方法返回的任务,则在 UI 线程中,UI 线程将被阻塞,直到完成。


您可能想使用TaskScheduler.Default,而不是TaskScheduler.Current. 如果这是在基于 UI 线程的 TaskScheduler 上调度的 Task 中调用的,它将在 UI 线程上自行调度。

于 2012-06-05T21:53:14.910 回答
0

希望我可以将格式化的代码放在评论中,但由于我不知道如何添加这个片段作为答案。这是我用来确定任务是否在 UI 线程上运行的方法(因为您不希望它运行)并使操作完全不同(简单的 thread.sleep)。

var state = new object();
var cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = cancellationTokenSource.Token;

var task = Task.Factory.StartNew(
    objState => { Console.WriteLine ("Current thread is {0}", Thread.CurrentThread.ManagedThreadId); Thread.Sleep(30); },
    state,
    cancellationToken,
    TaskCreationOptions.LongRunning,
    TaskScheduler.Current);

task.Wait();    
于 2012-06-06T13:42:32.267 回答