Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/261.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/api/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# .NET核心API似乎处于死锁状态_C#_Api_.net Core_Azure Web App Service - Fatal编程技术网

C# .NET核心API似乎处于死锁状态

C# .NET核心API似乎处于死锁状态,c#,api,.net-core,azure-web-app-service,C#,Api,.net Core,Azure Web App Service,我们有一个在生产中运行的.NET核心API,它可以稳定运行几天甚至几周,然后突然冻结。这种冻结甚至可能一天发生多次,完全是随机的。发生的情况:代码似乎已冻结,不接受任何新请求。没有新的请求被记录,线程数上升到天高,内存稳步上升,直到达到最大值 我创建了一个内存转储来进行分析。它告诉我,大多数线程都在等待某个特定函数的锁被释放,看起来像是死锁。我分析了这个函数,不明白为什么会出现问题。有人能帮我吗?显然,我怀疑AsParallel()是线程不安全的,但互联网说不,它是线程安全的 public as

我们有一个在生产中运行的.NET核心API,它可以稳定运行几天甚至几周,然后突然冻结。这种冻结甚至可能一天发生多次,完全是随机的。发生的情况:代码似乎已冻结,不接受任何新请求。没有新的请求被记录,线程数上升到天高,内存稳步上升,直到达到最大值

我创建了一个内存转储来进行分析。它告诉我,大多数线程都在等待某个特定函数的锁被释放,看起来像是死锁。我分析了这个函数,不明白为什么会出现问题。有人能帮我吗?显然,我怀疑AsParallel()是线程不安全的,但互联网说不,它是线程安全的

public async Task<bool> TryStorePropertiesAsync(string sessionId, Dictionary<string, string> keyValuePairs, int ttl = 1500)
{
    try
    {
        await Task.WhenAll(keyValuePairs.AsParallel().Select(async item => 
        {
            var doc = await _cosmosDbRepository.GetItemByKeyAsync(GetId(sessionId, item.Key), sessionId) ?? new Document();

            doc.SetPropertyValue("_partitionKey", sessionId);
            doc.SetPropertyValue("key", GetId(sessionId, item.Key));
            doc.SetPropertyValue("name", item.Key.ToLowerInvariant());
            doc.SetPropertyValue("value", item.Value);
            doc.TimeToLive = ttl;
            await _cosmosDbRepository.UpsertDocumentAsync(doc, "_partitionKey");
        }));
        return true;
    }
    catch
    {
        ApplicationInsightsLogger.TrackException(ex, new Dictionary<string, string>
        {
            { "sessionID", sessionId },
            { "action", "TryStoreItems" }
        });
        return false;
    }
}
public异步任务TryStorePropertiesAsync(字符串sessionId,字典keyValuePairs,int ttl=1500)
{
尝试
{
wait Task.WhenAll(keyValuePairs.aspallel().Select(异步项=>
{
var doc=await_cosmosdbrespository.GetItemByKeyAsync(GetId(sessionId,item.Key),sessionId)??新文档();
doc.SetPropertyValue(“\u partitionKey”,sessionId);
doc.SetPropertyValue(“key”,GetId(sessionId,item.key));
doc.SetPropertyValue(“name”,item.Key.ToLowerInvariant());
单据设置属性值(“值”,项目值);
doc.TimeToLive=ttl;
等待cosmosdbrespository.upsertdocumentsync(doc,“\u partitionKey”);
}));
返回true;
}
抓住
{
ApplicationInsightsLogger.TrackException(例如,新字典
{
{“sessionID”,sessionID},
{“动作”,“幽会项目”}
});
返回false;
}
}

该代码存在严重问题。对于例如100个项目,它触发100个并发操作,每次4/8。循环中的代码似乎从CosmosDB读取一个文档,设置其所有属性,然后调用一个名为“类似”的方法,而不需要预加载任何内容。如果不知道什么是宇宙假设及其方法,人们只能猜测。虽然在加载/更新周期(可能是无用的)发生时试图锁定东西,但它可能会产生额外的冲突

对于初学者来说,
AsParallel()
仅用于数据并行:在内存中划分一些数据并使用尽可能多的工作线程,因为每个分区都有内核来处理。这里没有数据,只是对异步操作的调用。这就是为什么对于100个项目,此代码将触发100个并发任务

这可能会达到任何数量的CosmosDB限制,即使它不会导致并发冲突。这也可能导致网络问题,因为所有这些并发连接都使用同一根电缆

不考虑CosmosDB,对远程服务进行大量调用的正确方法是将它们排队,并使用有限数量的工作人员执行它们。这很容易用.NET实现。代码可以更改为以下内容:

class Payload
{
    public string SessionKey{get;set;}
    public string Key{get;set;}
    public string Name{get;set;}
    public string Value{get;set;}
    public int TTL{get;set;}
}

//Allow only 10 concurrent upserts
var options=new ExecutionDataflowBlockOptions
  {
     MaxDegreeOfParallelism = 10
  };
var upsertBlock=new ActionBlock<Payload>(myPosterAsync,options);

foreach(var payload in payloads)
{
    block.Post(pair);
}
//Tell the block we're done
block.Complete();
//Await for all queued operations to complete
await block.Completion;

你的线程在等待哪个锁?你想做什么?并行执行不能加快错误查询的速度。不过,你可以让它们无限慢。如果执行eg 100 single row SELECTs而不是一次检索所有行的单选,则性能会降低100倍。如果您尝试并行执行此操作,您可能会更快或更慢地完成,因为您的100个单独连接相互冲突,并且可能会更新操作为什么还要使用
aspallel()
?老实说,这不是我的代码。所以我不知道他们为什么使用Parallel.ForEach,但我当然能想到一个原因。我更喜欢循环浏览它们,将任务添加到列表中,然后在最后等待列表。我可以也会这样做,但我想了解是什么导致API停止响应
aspallel()
是为了数据并行——使用所有内核处理大量内存中的数据。这里不是这样,没有数据。只是IO操作。它也没有做任何有用的事情,真正的异步操作是由
GetItemByKeyAsync
upsertDocumentSync
执行的。您可以只使用
someValues.Select(async…)
我现在所做的只是删除aspallel()并用一个简单的Select替换它。这就成功了,它现在稳定运行了将近两周。这意味着AsParallel()会造成死锁,不应该在异步场景中使用。显然我可以接受,但我仍然不明白为什么它会导致僵局。@Peter我已经在问题的评论中解释过了。它旨在实现数据并行,而不是异步操作。它不会死锁,它使用所有可用的内核来处理数据。这意味着,它也使用当前线程。如果没有
asparle()
您的代码将一个接一个地创建任务,但从不等待任务完成,直到
等待任务。whalll()
等待所有任务。另一方面,
aspallel()
一次创建4个或8个相同的任务,也不用等待它们完成。这是等待任务。当所有的人都阻止了汉克斯·帕纳吉奥蒂斯。您之前的评论确实解释了Asparell与Select以及性能差异,但您最后的评论让我意识到实际出了什么问题。非常感谢你
async Task myPosterAsync(Payload item)
{
    try
    {
        var doc = await _cosmosDbRepository.GetItemByKeyAsync(GetId(item.SessionId, item.Key), 
                                                          item.SessionId) 
                         ?? new Document();

        doc.SetPropertyValue("_partitionKey", item.SessionId);
        doc.SetPropertyValue("key", GetId(sessionId, item.Key));
        doc.SetPropertyValue("name", item.Name);
        doc.SetPropertyValue("value", item.Value);
        doc.TimeToLive = item.TTL;
        await _cosmosDbRepository.UpsertDocumentAsync(doc, "_partitionKey");
    catch (Exception ex)   
    {
        //Handle the error in some way, eg log it
        ApplicationInsightsLogger.TrackException(ex, new Dictionary<string, string>
        {
            { "sessionID", item.SessionId },
            { "action", "TryStoreItems" }
        });
    }
}