C# 为什么长时间运行的任务仍然会阻塞UI?

C# 为什么长时间运行的任务仍然会阻塞UI?,c#,wpf,task-parallel-library,C#,Wpf,Task Parallel Library,我试图解决一个问题,我的UI被阻止,我不明白为什么 public Task AddStuff(string myID, List<string> otherIDs) { Action doIt = () => { this.theService.AddStuff(myID, otherIDs); }; return Task.Factory.StartNew(doIt, TaskCreationOptions.LongRunni

我试图解决一个问题,我的UI被阻止,我不明白为什么

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

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

private void LoadAssets(对象对象对象状态)
{
LoadAssetState laState=(LoadAssetState)对象状态;
List assetIDs=新列表();
对于(int i=0;i
{
if(laState.CancellationToken.IsCancellationRequested)
返回;
App.ApiContext.GetOrderDetails(laState.ActiveOrder.OrderID,false,laState.CancellationToken)。ContinueWith(orderDetailsTask=>
{
if(laState.CancellationToken.IsCancellationRequested)
返回;
this.activeOrder=orderDetailsTask.Result;
this.StandardPrintProductsStep.Synchronize(this.activeOrder);
});
});
}

公共任务AddAssetToProduct(字符串orderID、字符串lineID、列表资产ID、整数数量、CancellationToken?CancellationToken=null) { 动作doIt=()=> { if(cancellationToken.IsCancellationRequested()) 返回; this.ordersService.AddAssetToProduct(orderID、lineID、AssetId、quantity); }; if(cancellationToken!=null) 返回Task.Factory.StartNew(doIt,cancellationToken.Value,TaskCreationOptions.LongRunning,TaskScheduler.Current); 其他的 返回Task.Factory.StartNew(doIt,TaskCreationOptions.LongRunning); }
编辑

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

听起来似乎没有理由阻止它,所以我想如果列表很长,我会把它分解,然后打多个电话。我只是想确保我的任务逻辑没有遗漏什么

有没有其他方法可以做到这一点,这样它就不会阻塞UI

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

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



您可能想使用,而不是
TaskScheduler.Current
。如果在基于UI线程的TaskScheduler上调度的任务中调用此函数,它将在UI线程上调度自身。

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


根本原因不在显示的代码中。你能显示AddStuff的调用者吗?
this.theService.AddStuff(myID,otherid)
与公共任务AddStuff(string myID,List otherid)相同。如果是这样,那么由于无限递归,您将创建无限多的任务!当你准备好全力时,为什么不打一下用户界面呢。不用打几个小拳头。这些小动作也会导致UI响应。这是在更新进度条,但直到最后的服务调用,UI才会被阻止。我会通过将其分解为多个步骤来找出为什么会阻止UI线程而不是“punting”。例如,让动作1)只执行一个线程。改为睡眠,2)打印线程id以查看它是否为“1”-听起来像TaskScheduler。不过,默认值是更好的选择?没有骰子,使用默认值而不是当前值的行为相同way@JoshRusso你能在VisualStudio的并发可视化工具下运行这个吗?您可以通过这种方式中断并精确显示阻止主线程的内容…@JoshRusso否-它在分析窗口中-启动性能向导(VS 2010)-在VS 2012中,它是Analyze->Concurrency->。。。它只适用于更高的SKU。你也可以只看threads窗口,但如果线程被阻塞,它并不总是正常工作。是的,我认为我们没有足够高的版本来实现这一点。此外,我们实际上将在下周交付代码,我非常确信是他们的API导致了阻塞。所以我给了它一个创可贴,把名单分开,打一系列小电话。
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);
}
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();