如何在请求开始的同一线程上调用代码

时间:2017-03-02 18:15:20

标签: c# multithreading asp.net-web-api newrelic

我在许多服务中使用async await的POST控制器方法,在控制器级别我需要发送一些New Relic参数。 当从除开始的请求之外的线程发送参数时,New Relic会提供警告日志。

  

NewRelic警告:代理API错误:调用API方法时出错   " AddCustomParameter" - " System.InvalidOperationException:API   调用的方法仅在事务中有效。这个错误可以   如果您从一个以外的线程调用API方法,则会发生   交易开始了。在   NewRelic.Agent.Core.Api.AsyncAgentApi.GetCurrentTransactionBuilder()
  at NewRelic.Agent.Core.Api.AsyncAgentApi.AddCustomParameter(String   key,String value)"

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

示例,控制器中的以下代码。

test = Result(np.random.randn(3,4), columns=['A','B','C','D']) #just an example
test_2 = test.new_filter()

将打印

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)}");
}

Per New Relic警告日志我应该在线程ID 5中调用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 方法。

AddCustomParameterAddPoliciesCountInNewRelic调用AddTotalTransactionsCountInNewRelic基类保护方法。

ApiControllerBase.AddNewRelicParameter(string, string)

2 个答案:

答案 0 :(得分:1)

我能够使用continuation来解决这个问题,但是像@Tim在@Tim中回答的那样,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

答案 1 :(得分:0)

如果你必须回到同一个线程,那么你真的无法退出该线程 - 无法保证你的线程会回复给你。 Async ... await将正确恢复操作请求的上下文,但不一定是同一个线程。

换句话说,要么不做异步...等待调用并保留你的线程,要么在你的控制器方法中启动你的异步代码,然后阻塞并等待它完成(从而挫败异步的目的) ..await代码,因为在等待IO完成时你不会让另一个动作方法的线程产生)