Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.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# 从Parallel.ForEach中捕获完整的调用堆栈_C#_.net_Multithreading - Fatal编程技术网

C# 从Parallel.ForEach中捕获完整的调用堆栈

C# 从Parallel.ForEach中捕获完整的调用堆栈,c#,.net,multithreading,C#,.net,Multithreading,我在一个低级库中有一些代码,需要知道调用链中某个类是否在它上面(我们正在从一个数据存储迁移到另一个数据存储,如果有人不通过迁移对象直接访问旧存储,我需要发出警报)。通常我只会检查堆栈跟踪,但有时会从Parallel.ForEach中调用此库中的方法,这会中断堆栈跟踪,因为大多数线程都在线程池的上下文中运行 以下是一个例子: using System; using System.Collections.Concurrent; using System.Linq; using System.Thre

我在一个低级库中有一些代码,需要知道调用链中某个类是否在它上面(我们正在从一个数据存储迁移到另一个数据存储,如果有人不通过迁移对象直接访问旧存储,我需要发出警报)。通常我只会检查堆栈跟踪,但有时会从
Parallel.ForEach
中调用此库中的方法,这会中断堆栈跟踪,因为大多数线程都在线程池的上下文中运行

以下是一个例子:

using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace CallstackSpike
{
    public class Program
    {
        public static void Main()
        {
            new MigrationUtil().DoWork(10);
        }
    }

    internal class MigrationUtil
    {
        public void DoWork(int workCount)
        {
            new Fetcher().FetchStuff(workCount);
        }
    }

    internal class Fetcher
    {
        private readonly Worker worker = new Worker();

        public void FetchStuff(int workCount)
        {
            Console.WriteLine("Trying parallel foreach... ");
            ConcurrentBag<bool> results = new ConcurrentBag<bool>();
            Parallel.ForEach(
                Enumerable.Range(0, workCount),
                new ParallelOptions { MaxDegreeOfParallelism = 4 },
                idx =>
                {
                    results.Add(worker.DoWork(idx));
                });

            bool result = results.Aggregate(true, (current, res) => current & res);
            Console.WriteLine("result: " + result);
        }
    }

    internal class Worker
    {
        private readonly Random random = new Random();

        public bool DoWork(int idx)
        {
            Thread.Sleep(random.Next(100, 500));

            string stackTrace = Environment.StackTrace;
            Console.WriteLine("IDX = {0}\n{1}\n", idx, stackTrace);

            return stackTrace.Contains("MigrationUtil");
        }
    }
}
剩余25%的时间我可以看到完整的调用堆栈:

位于System.Environment.GetStackTrace(异常e,布尔needFileInfo)
在System.Environment.get_StackTrace()中
在C:\Dev\Spikes\CallstackSpike\CallstackSpike\Program.cs中的CallstackSpike.Worker.DoWork(Int32 idx)处:第56行
在c:\Dev\Spikes\CallstackSpike\CallstackSpike\Program.cs中的CallstackSpike.Fetcher.c\uu显示Class1\u 0.b\u0(Int32 idx):第41行
在System.Threading.Tasks.Parallel.c__uDisplayClass42_0`2.b_u 1()中
在System.Threading.Tasks.Task.InnerInvoke()中
位于System.Threading.Tasks.Task.InnerInvokeWitchArg(任务子任务)
在System.Threading.Tasks.Task.c\u显示器上显示Class176\u 0.b\u 0(对象)
位于System.Threading.Tasks.Task.ExecuteSelfReplicating(任务根目录)
在System.Threading.Tasks.Task.Execute()中
位于System.Threading.Tasks.Task.ExecutionContextCallback(对象obj)
位于System.Threading.ExecutionContext.RunInternal(ExecutionContext ExecutionContext、ContextCallback回调、对象状态、布尔值preserveSyncCtx)
在System.Threading.ExecutionContext.Run(ExecutionContext ExecutionContext,ContextCallback回调,对象状态,布尔保存SyncCTX)
位于System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task和currentTaskSlot)
在System.Threading.Tasks.Task.ExecuteEntry(布尔bPreventDoubleExecution)
位于System.Threading.Tasks.ThreadPoolTaskScheduler.TryExecuteTaskInline(任务任务,布尔任务先前已排队)
位于System.Threading.Tasks.TaskScheduler.TryRunLine(任务任务,布尔任务先前已排队)
在System.Threading.Tasks.Task.InternalRunSynchronously(TaskScheduler调度程序,布尔waitForCompletion)
在System.Threading.Tasks.Task.RunSynchronously(TaskScheduler)上运行
在System.Threading.Tasks.Parallel.PartitionerForEachWorker[TSource,TLocal](分区器'1 source,ParallelOptions ParallelOptions,Action'1 simpleBody,Action'2 bodyWithState,Action'3 bodyWithStateAndIndex,Func'4 bodyWithStateAndLocal,Func'5 bodyWithEverything,Func'1 localInit,Action'1 localFinally)
在System.Threading.Tasks.Parallel.ForEachWorker[TSource,TLocal](IEnumerable`1 source,ParallelOptions ParallelOptions,Action`1 body,Action`2 bodyWithState,Action`3 bodyWithStateAndIndex,Func`4 bodyWithStateAndLocal,Func`5 bodyWithEverything,Func`1 localInit,Action`1 localFinally)
在System.Threading.Tasks.Parallel.ForEach[TSource](IEnumerable`1源,ParallelOptions,Action`1体)
在C:\Dev\Spikes\CallstackSpike\CallstackSpike\Program.cs中的CallstackSpike.Fetcher.FetchStuff(Int32 workCount)处:第39行
在C:\Dev\Spikes\CallstackSpike\CallstackSpike\Program.cs中的CallstackSpike.MigrationUtil.DoWork(Int32 workCount)处:第27行
在C:\Dev\Spikes\CallstackSpike\CallstackSpike\Program.cs中的CallstackSpike.Program.Main()处:第13行

所以我的问题是,有没有办法查询当前线程,以了解它是否是由于一个
Parallel.ForEach
引起的,如果是,它是从哪里来的?某个地方一定有人知道,我只是不知道当前线程是否有权访问该信息。

可能会在较高级别的调用中锁定某个变量,然后从较低级别的调用中查看是否有锁,如果是这样的话,那么您知道使用的是较高的线程了吗?请注意:在一种情况下,您会看到完整的堆栈,因为Parallel.ForEach重用当前线程来执行一个操作(因为该线程在等待ForEach完成时会被阻塞,所以为什么不将其用于有用的工作)。顺便说一句,我不认为您想要的是可能的,因为没有“父”线程的概念,所以您无法确定哪个线程“创建”了当前线程(除非您在创建线程时自己管理此信息)。但是对于parallel.foreach,一个线程总是在调用它的线程上运行,这还不够吗?您只能在该线程上引发异常。不幸的是,从另一个线程获取StackTrace的函数(您可能已将其作为Dowork()参数提供)已过时。唯一的解决办法是对结果进行后处理。例如,如果最后一行包含“PerformWaitCallback()”,则添加当前堆栈跟踪。您可以将堆栈跟踪传递到Parallel.ForEach,然后从所需的帧将其附加到当前堆栈跟踪。这将有点棘手,但可以做到。也许在较高级别的调用中对变量设置锁,然后从较低级别的调用中查看锁是否存在,如果存在,那么您就知道较高级别的正在使用?请注意:在一种情况下,您会看到完整的堆栈,因为Parallel.ForEach重用当前线程来执行一个操作(因为这个线程在等待foreach完成时会被阻塞,所以为什么不使用它进行有用的工作呢)。顺便说一句,我不认为你想要什么是可能的,因为没有“父”线程的概念,所以你无法确定是哪个线程“创建”了当前线程(除非你在创建线程时自己管理这个信息).但对于parallel.foreach,一个线程总是在调用它的线程上运行,这还不够吗?您只能在该线程上引发异常。从另一个线程获取StackTrace的函数-您可能已作为Dowork()提供很遗憾,参数-已过时。唯一的解决方案是对结果进行后处理。例如,如果最后一行包含“PerformWaitCallback()”,请添加当前堆栈跟踪。您可以将堆栈跟踪传递到Parallel.ForEach,然后从所需帧将其附加到当前堆栈跟踪。它将点亮
at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo)
at System.Environment.get_StackTrace()
at CallstackSpike.Worker.DoWork(Int32 idx) in C:\Dev\Spikes\CallstackSpike\CallstackSpike\Program.cs:line 56
at CallstackSpike.Fetcher.<>c__DisplayClass1_0.<FetchStuff>b__0(Int32 idx) in C:\Dev\Spikes\CallstackSpike\CallstackSpike\Program.cs:line 41
at System.Threading.Tasks.Parallel.<>c__DisplayClass42_0`2.<PartitionerForEachWorker>b__1()
at System.Threading.Tasks.Task.InnerInvoke()
at System.Threading.Tasks.Task.InnerInvokeWithArg(Task childTask)
at System.Threading.Tasks.Task.<>c__DisplayClass176_0.<ExecuteSelfReplicating>b__0(Object )
at System.Threading.Tasks.Task.InnerInvoke()
at System.Threading.Tasks.Task.Execute()
at System.Threading.Tasks.Task.ExecutionContextCallback(Object obj)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot)
at System.Threading.Tasks.Task.ExecuteEntry(Boolean bPreventDoubleExecution)
at System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
at System.Threading.ThreadPoolWorkQueue.Dispatch()
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo)
at System.Environment.get_StackTrace()
at CallstackSpike.Worker.DoWork(Int32 idx) in C:\Dev\Spikes\CallstackSpike\CallstackSpike\Program.cs:line 56
at CallstackSpike.Fetcher.<>c__DisplayClass1_0.<FetchStuff>b__0(Int32 idx) in C:\Dev\Spikes\CallstackSpike\CallstackSpike\Program.cs:line 41
at System.Threading.Tasks.Parallel.<>c__DisplayClass42_0`2.<PartitionerForEachWorker>b__1()
at System.Threading.Tasks.Task.InnerInvoke()
at System.Threading.Tasks.Task.InnerInvokeWithArg(Task childTask)
at System.Threading.Tasks.Task.<>c__DisplayClass176_0.<ExecuteSelfReplicating>b__0(Object )
at System.Threading.Tasks.Task.ExecuteSelfReplicating(Task root)
at System.Threading.Tasks.Task.Execute()
at System.Threading.Tasks.Task.ExecutionContextCallback(Object obj)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot)
at System.Threading.Tasks.Task.ExecuteEntry(Boolean bPreventDoubleExecution)
at System.Threading.Tasks.ThreadPoolTaskScheduler.TryExecuteTaskInline(Task task, Boolean taskWasPreviouslyQueued)
at System.Threading.Tasks.TaskScheduler.TryRunInline(Task task, Boolean taskWasPreviouslyQueued)
at System.Threading.Tasks.Task.InternalRunSynchronously(TaskScheduler scheduler, Boolean waitForCompletion)
at System.Threading.Tasks.Task.RunSynchronously(TaskScheduler scheduler)
at System.Threading.Tasks.Parallel.PartitionerForEachWorker[TSource,TLocal](Partitioner`1 source, ParallelOptions parallelOptions, Action`1 simpleBody, Action`2 bodyWithState, Action`3 bodyWithStateAndIndex, Func`4 bodyWithStateAndLocal, Func`5 bodyWithEverything, Func`1 localInit, Action`1 localFinally)
at System.Threading.Tasks.Parallel.ForEachWorker[TSource,TLocal](IEnumerable`1 source, ParallelOptions parallelOptions, Action`1 body, Action`2 bodyWithState, Action`3 bodyWithStateAndIndex, Func`4 bodyWithStateAndLocal, Func`5 bodyWithEverything, Func`1 localInit, Action`1 localFinally)
at System.Threading.Tasks.Parallel.ForEach[TSource](IEnumerable`1 source, ParallelOptions parallelOptions, Action`1 body)
at CallstackSpike.Fetcher.FetchStuff(Int32 workCount) in C:\Dev\Spikes\CallstackSpike\CallstackSpike\Program.cs:line 39
at CallstackSpike.MigrationUtil.DoWork(Int32 workCount) in C:\Dev\Spikes\CallstackSpike\CallstackSpike\Program.cs:line 27
at CallstackSpike.Program.Main() in C:\Dev\Spikes\CallstackSpike\CallstackSpike\Program.cs:line 13