Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/327.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# 如何在请求启动的同一线程上调用代码_C#_Multithreading_Asp.net Web Api_Newrelic - Fatal编程技术网

C# 如何在请求启动的同一线程上调用代码

C# 如何在请求启动的同一线程上调用代码,c#,multithreading,asp.net-web-api,newrelic,C#,Multithreading,Asp.net Web Api,Newrelic,在许多服务中,我有一个使用async await的后控制器方法,在控制器级别,我需要发送一些新的Relic参数。 当参数不是从已启动请求的线程发送时,New Relic会给出警告日志 NewRelic警告:代理API错误:调用API方法时出错 “AddCustomParameter”-“System.InvalidOperationException:API 调用的方法仅在事务中有效。此错误可能会导致 如果从线程以外的线程调用API方法,则发生 事务开始于。在 NewRelic.Agent.Co

在许多服务中,我有一个使用async await的后控制器方法,在控制器级别,我需要发送一些新的Relic参数。 当参数不是从已启动请求的线程发送时,New Relic会给出警告日志

NewRelic警告:代理API错误:调用API方法时出错 “AddCustomParameter”-“System.InvalidOperationException:API 调用的方法仅在事务中有效。此错误可能会导致 如果从线程以外的线程调用API方法,则发生 事务开始于。在 NewRelic.Agent.Core.Api.AsyncAgentApi.GetCurrentTransactionBuilder()
位于NewRelic.Agent.Core.Api.AsyncAgentApi.AddCustomParameter(字符串 键,字符串值)

如何在控制器方法中调用将参数值发送到New Relic的代码

例如,控制器中的以下代码

var threadid = Thread.CurrentThread.ManagedThreadId;
Log.Debug($"Before async method : {ThreadIdMessage(threadid)}");
var reportObject = await ReportService.GetReportAsync(requestModel).ConfigureAwait(true);
if (reportObject.PolicyModels != null)
{
    threadid = Thread.CurrentThread.ManagedThreadId;
    Log.Debug($"Before sending New Relic values: {ThreadIdMessage(threadid)}");
    AddPoliciesCountInNewRelic(reportObject.PolicyModels.Count);
    AddTotalTransactionsCountInNewRelic(
                            reportObject.PolicyModels.SelectMany(p => p.PolicyTransactionModels).Count());
    threadid = Thread.CurrentThread.ManagedThreadId;
    Log.Debug($"After sending New Relic values: {ThreadIdMessage(threadid)}");
}
将打印

DEBUG - Before async method : Current Thread Id: 5  
DEBUG - Before sending New Relic values: Current Thread Id: 9
NewRelic.AddCustomParameter(CVPoliciesCount,2)
NewRelic.AddCustomParameter(CVTotalTransactionsCount,8)  
DEBUG - After sending New Relic values: Current Thread Id: 9
根据NewRelic警告日志,我应该调用线程id 5中的
AddCustomParameter
方法

AddPoliciesCountInNewRelic
addTotalTransactionCountinnewrelic
调用
ApiControllerBase.AddNewRelicParameter(string,string)
基类保护方法

private void AddPoliciesCountInNewRelic(int policiesCount)
{
    AddNewRelicParameter("CVPoliciesCount", policiesCount.ToString());
}

private void AddTotalTransactionsCountInNewRelic(int transactionsCount)
{
    AddNewRelicParameter("CVTotalTransactionsCount", transactionsCount.ToString());
}

protected void AddNewRelicParameter(string key, string value)
{
    if (!string.IsNullOrWhiteSpace(key) &&
        !string.IsNullOrWhiteSpace(value))
    {
        try
        {
            NewRelic.Api.Agent.NewRelic.AddCustomParameter(key, value);
        }
        catch (Exception ex)
        {
            Log.Error($"ERROR! : New Relic Parameter Exception {ex}");
        }
    }
}

如果您必须返回同一个线程,那么您真的无法退出该线程——无法保证您的线程会返回给您。异步。。。wait将正确恢复操作请求的上下文,但不一定是同一线程


换句话说,要么不进行异步…等待调用并保留线程,要么在控制器方法中启动异步代码,但阻止并等待它完成(从而破坏异步..等待代码的目的,因为在等待IO完成时,您不会让线程执行另一个操作方法)

我可以通过使用continuation来解决这个问题,但就像@Tim answer中的@evk所评论的那样,New Relic应该考虑解决这个问题,这样我们就不必使用合适的代码来满足这样的要求

    var context = TaskScheduler.FromCurrentSynchronizationContext();    
    var threadid = Thread.CurrentThread.ManagedThreadId;
    Log.Debug($"Entry ThreadID: {threadid}");
    var getReportTask = ReportService.GetReportAsync(requestModel);
    getReportTask.ContinueWith(antecedent =>
    {
        var continuationThreadid = Thread.CurrentThread.ManagedThreadId;
        Log.Debug($"continuationThreadid: {continuationThreadid}");
        var result = antecedent.Result;
        if (result.PolicyModels != null)
        {
            AddPoliciesCountInNewRelic(result.PolicyModels.Count);
            AddTotalTransactionsCountInNewRelic(
                result.PolicyModels.SelectMany(p => p.PolicyTransactionModels).Count());
        }
    }, context);

    var reportObject = await getReportTask.ConfigureAwait(false);
它将按预期打印相同的线程id

DEBUG - Entry ThreadID: 5 
DEBUG - continuationThreadid: 5

<他们声称现在支持异步控制器。
ConfigureAwait(true)
在asp.net中不能保证您回到启动调用的同一线程,就像您在Winforms或WPF中使用UI线程时一样。事实上,
ConfigureAwait(true)
是默认行为。这与wait ReportService.GetReportAsync(requestModel)完全相同。代码不显示对NewRelic的任何调用。请发布代码,说明如何创建事务以及如何调用
AddCustomParameters
。这个信息正确吗?是否存在活动事务?@PanagiotisKanavos该代码以调用AddCustomParameter的方式更新。我没有设置任何事务,但我看到New Relic根据应用程序的方法为我提供了一个bucket。而且它现在看起来好像在工作!此外,NewRelic停止了对警告的日志记录。或者向NewRelic提交一个问题,并要求他们修复代码。@evk正好…由于他们的要求,无法击败我的编程。在这种情况下,为什么不使用API的同步版本呢?这样,您就不会冒另一个请求占用线程并迫使您等待的风险。PS-在ASP.NET中,同步上下文不是线程。您可能仍然会得到一个不同的线程。@PanagiotisKanavos,您确实得到了。我自己试过了,但续集不在最初的线程上。SynchronizationContext是AspNetSynchronizationContext,如果您查找post方法的源代码,它将导致使用TaskScheduler.Default