9

कृपया पृष्ठभूमि जानकारी के लिए इस सवाल का देखें:CorrelationManager.LogicalOperationStack Parallel.For, कार्य, धागे, आदि के साथ संगत है

How do Tasks in the Task Parallel Library affect ActivityID?

यही प्रश्न पूछता कैसे कार्य Trace.CorrelationManager.ActivityId प्रभावित करते हैं। @ ग्रेग सैमसन ने एक परीक्षण कार्यक्रम के साथ अपने स्वयं के प्रश्न का उत्तर दिया कि गतिविधि आईडी कार्य के संदर्भ में विश्वसनीय है। परीक्षण कार्यक्रम टास्क प्रतिनिधि की शुरुआत में एक गतिविधि आईडी सेट करता है, काम को अनुकरण करने के लिए सोता है, फिर यह सुनिश्चित करने के लिए गतिविधि आईडी को अंत में जांचता है कि यह वही मान है (यानी कि यह किसी अन्य थ्रेड द्वारा संशोधित नहीं किया गया है)। कार्यक्रम सफलतापूर्वक चलाता है।

सूत्रण, कार्य, और समानांतर आपरेशन (अंत में प्रवेश के लिए बेहतर संदर्भ प्रदान करने) के लिए अन्य "संदर्भ" विकल्प पर शोध करते हुए मैं Trace.CorrelationManager.LogicalOperationStack साथ एक अजीब मुद्दा में भाग (यह वैसे भी मेरे लिए अजीब था)। मैंने नीचे अपने प्रश्न के लिए अपना "उत्तर" कॉपी किया है।

मुझे लगता है कि यह पर्याप्त रूप से मुद्दा यह है कि मैं में भाग का वर्णन करता है (Trace.CorrelationManager.LogicalOperationStack जाहिरा तौर पर भ्रष्ट हो रही है - या कुछ और - जब Parallel.For के संदर्भ में प्रयोग किया जाता है, लेकिन केवल तभी Parallel.For अपने आप में संलग्न है एक तार्किक ऑपरेशन)।

यहाँ मेरी प्रश्न हैं:

  1. Trace.CorrelationManager.LogicalOperationStack चाहिए Parallel.For साथ प्रयोग करने योग्य हो सकता है? यदि हां, तो क्या यह एक फर्क पड़ता है यदि एक तार्किक ऑपरेशन समानांतर के साथ पहले से ही प्रभावी है। शुरू हो गया है?

  2. क्या समानांतर के साथ LogicalOperationStack का उपयोग करने का एक "सही" तरीका है? के लिए? क्या मैं इस नमूना कार्यक्रम को अलग-अलग कोड कर सकता हूं ताकि यह "काम करता है"? "काम करता है" से मेरा मतलब है कि लॉजिकलऑपरेशनस्टैक में हमेशा प्रविष्टियों की अपेक्षित संख्या होती है और प्रविष्टियां स्वयं अपेक्षित प्रविष्टियां होती हैं।

मैं धागे और ThreadPool धागे का उपयोग कर कुछ अतिरिक्त परीक्षण किया है, लेकिन मैं वापस जाने के लिए और उन लोगों के परीक्षण पुन: प्रयास करता है, तो मैं इसी तरह की समस्याओं में भाग देखने के लिए करना होगा।

मैं कहूंगा कि यह प्रतीत होता है कि कार्य/समानांतर धागे और थ्रेडपूल धागे ट्रेस "उत्तराधिकारी" को "उत्तराधिकारी" करते हैं। सक्रियता आईडी और ट्रेस। सहसंबंध प्रबंधक। लॉजिकलऑपरेशन पैरेंट थ्रेड से मूल्यों को रोकें। यह अपेक्षा की जाती है क्योंकि इन मानों को CallContext की लॉजिकलसेटडेट विधि (सेटडाटा के विपरीत) का उपयोग करके सहसंबंध प्रबंधक द्वारा संग्रहीत किया जाता है।

फिर, "जवाब" है कि मैं नीचे पोस्ट के लिए मूल संदर्भ प्राप्त करने के लिए इस सवाल का वापस संदर्भ लें:

How do Tasks in the Task Parallel Library affect ActivityID?

भी देखें इस समान प्रश्न (जो अब तक उत्तर नहीं दिया गया) माइक्रोसॉफ्ट के समानांतर एक्सटेंशन फ़ोरम पर:

http://social.msdn.microsoft.com/Forums/en-US/parallelextensions/thread/7c5c3051-133b-4814-9db0-fc0039b4f9d9

[शुरू चिपकाएं]

कृपया इसे मेरे उत्तर को उत्तर के रूप में क्षमा करें क्योंकि यह वास्तव में आपके प्रश्न का उत्तर नहीं है, हालांकि, यह आपके प्रश्न से संबंधित है क्योंकि यह सहसंबंध प्रबंधक व्यवहार और धागे/कार्य/आदि से संबंधित है।मैं multithreading परिदृश्यों में अतिरिक्त संदर्भ प्रदान करने के लिए CorrelationManager के LogicalOperationStack (और StartLogicalOperation/StopLogicalOperation विधियों) का उपयोग कर देख रहा हूं।

मैंने आपका उदाहरण लिया और समानांतर में समानांतर में काम करने की क्षमता जोड़ने के लिए थोड़ा सा संशोधित किया। इसके लिए। इसके अलावा, मैं StartLogicalOperation/StopLogicalOperation का उपयोग ब्रैकेट (आंतरिक रूप से) DoLongRunningWork पर करता हूं।

DoLongRunningWork 
    StartLogicalOperation 
    Thread.Sleep(3000) 
    StopLogicalOperation 

मैं ने पाया है कि अगर मैं अपने कोड में ये तार्किक संचालन जोड़ने (कम या ज्यादा होता है), तार्किक operatins के सभी सिंक में रहते हैं (: वैचारिक रूप से, DoLongRunningWork इस हर बार निष्पादित किया जाता है की तरह कुछ करता है ढेर पर संचालन की हमेशा अपेक्षित संख्या और ढेर पर संचालन के मूल्य हमेशा अपेक्षित होते हैं)।

मेरे अपने कुछ परीक्षणों में मैंने पाया कि यह हमेशा मामला नहीं था। तार्किक ऑपरेशन स्टैक "दूषित" हो रहा था। सबसे अच्छा स्पष्टीकरण मैं साथ आ सकता हूं कि "बच्चे" धागे से निकलने पर कॉलकॉन्टेक्स्ट जानकारी की "विलय" वापस "मूल" थ्रेड संदर्भ में "पुरानी" बाल थ्रेड संदर्भ जानकारी (लॉजिकल ऑपरेशन) " विरासत "एक और भाई बच्चे धागे द्वारा।

समस्या भी "कार्यकर्ता धागे" में से एक (या जो भी वे में बुलाया जाना चाहिए के रूप में तथ्य यह है कि Parallel.For जाहिरा तौर पर मुख्य थ्रेड (लिखित रूप में कम से कम उदाहरण के कोड में) का उपयोग करता है से संबंधित हो सकती समांतर डोमेन)। जब भी DoLongRunningWork निष्पादित किया जाता है, तो एक नया लॉजिकल ऑपरेशन शुरू होता है (शुरुआत में) और बंद हो जाता है (अंत में) (यानी, लॉजिकलऑपरेशनस्टैक पर धक्का दिया जाता है और इसके पीछे से पॉप किया जाता है)। यदि मुख्य धागे में पहले से ही एक तार्किक ऑपरेशन है और यदि डॉन लोंगरिंग वर्क मुख्य थ्रेड पर निष्पादित करता है, तो एक नया लॉजिकल ऑपरेशन शुरू हो गया है, इसलिए मुख्य थ्रेड के लॉजिकलऑपरेशनस्टैक में अब दो ऑपरेशन हैं। DoLongRunningWork के किसी भी बाद के निष्पादन (जब तक DoLongRunningWork का यह "पुनरावृत्ति" मुख्य धागे पर निष्पादित हो रहा है) (जाहिर है) मुख्य थ्रेड के लॉजिकलऑपरेशनस्टैक (जो अब केवल एक अपेक्षित ऑपरेशन के बजाए दो ऑपरेशन है) का वारिस करेगा।

मुझे यह पता लगाने में काफी समय लगा कि लॉजिकलऑपरेशनस्टैक का व्यवहार आपके उदाहरण के मेरे संशोधित संस्करण की तुलना में मेरे उदाहरण में अलग क्यों था। आखिरकार मैंने देखा कि मेरे कोड में मैंने पूरे कार्यक्रम को लॉजिकल ऑपरेशन में ब्रैकेट किया था, जबकि आपके टेस्ट प्रोग्राम के मेरे संशोधित संस्करण में मैंने नहीं किया था। निहितार्थ यह है कि मेरे परीक्षण कार्यक्रम में, हर बार जब मेरा "काम" किया गया था (DoLongRunningWork के समान), पहले से ही एक तार्किक ऑपरेशन प्रभावी था। आपके परीक्षण कार्यक्रम के मेरे संशोधित संस्करण में, मैंने पूरे कार्यक्रम को लॉजिकल ऑपरेशन में ब्रैकेट नहीं किया था।

इसलिए, जब मैंने आपके परीक्षण कार्यक्रम को लॉजिकल ऑपरेशन में पूरे प्रोग्राम को ब्रैकेट करने के लिए संशोधित किया और यदि मैं समांतर का उपयोग कर रहा हूं। इसके लिए, मैं बिल्कुल वही समस्या में भाग गया।

ऊपर वैचारिक मॉडल का उपयोग करना, यह सफलतापूर्वक चलेंगे:

Parallel.For 
    DoLongRunningWork 
    StartLogicalOperation 
    Sleep(3000) 
    StopLogicalOperation 

यह अंततः जाहिरा तौर पर सिंक LogicalOperationStack के बाहर एक के कारण जोर करते हैं:

StartLogicalOperation 
Parallel.For 
    DoLongRunningWork 
    StartLogicalOperation 
    Sleep(3000) 
    StopLogicalOperation 
StopLogicalOperation 

यहाँ मेरी नमूना कार्यक्रम है। यह आपके जैसा ही है कि इसमें एक DoLongRunningWork विधि है जो ActivityId के साथ-साथ LogicalOperationStack में हेरफेर करती है। मेरे पास DoLongRunningWork की लात मारने के दो स्वाद भी हैं। एक स्वाद समान कार्यों का उपयोग करता है जो एक समानांतर का उपयोग करता है। के लिए। प्रत्येक स्वाद को भी निष्पादित किया जा सकता है कि संपूर्ण समांतर ऑपरेशन लॉजिकल ऑपरेशन में संलग्न है या नहीं। तो, समानांतर संचालन निष्पादित करने के कुल 4 तरीके हैं। प्रत्येक को आज़माने के लिए, वांछित "उपयोग करें ..." विधि को असम्बद्ध करें, पुनः संकलित करें और चलाएं।UseTasks, UseTasks(true), और UseParallelFor सभी को पूरा होने के लिए तैयार होना चाहिए। UseParallelFor(true) कुछ बिंदु पर जोर देगी क्योंकि LogicalOperationStack में प्रविष्टियों की अपेक्षित संख्या नहीं है।

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Diagnostics; 
using System.Threading; 
using System.Threading.Tasks; 

namespace CorrelationManagerParallelTest 
{ 
    class Program 
    {  
    static void Main(string[] args)  
    { 
     //UseParallelFor(true) will assert because LogicalOperationStack will not have expected 
     //number of entries, all others will run to completion. 

     UseTasks(); //Equivalent to original test program with only the parallelized 
         //operation bracketed in logical operation. 
     ////UseTasks(true); //Bracket entire UseTasks method in logical operation 
     ////UseParallelFor(); //Equivalent to original test program, but use Parallel.For 
          //rather than Tasks. Bracket only the parallelized 
          //operation in logical operation. 
     ////UseParallelFor(true); //Bracket entire UseParallelFor method in logical operation 
    }  

    private static List<int> threadIds = new List<int>();  
    private static object locker = new object();  

    private static int mainThreadId = Thread.CurrentThread.ManagedThreadId; 

    private static int mainThreadUsedInDelegate = 0; 

    // baseCount is the expected number of entries in the LogicalOperationStack 
    // at the time that DoLongRunningWork starts. If the entire operation is bracketed 
    // externally by Start/StopLogicalOperation, then baseCount will be 1. Otherwise, 
    // it will be 0. 
    private static void DoLongRunningWork(int baseCount)  
    { 
     lock (locker) 
     { 
     //Keep a record of the managed thread used.    
     if (!threadIds.Contains(Thread.CurrentThread.ManagedThreadId)) 
      threadIds.Add(Thread.CurrentThread.ManagedThreadId); 

     if (Thread.CurrentThread.ManagedThreadId == mainThreadId) 
     { 
      mainThreadUsedInDelegate++; 
     } 
     }   

     Guid lo1 = Guid.NewGuid(); 
     Trace.CorrelationManager.StartLogicalOperation(lo1); 

     Guid g1 = Guid.NewGuid();   
     Trace.CorrelationManager.ActivityId = g1; 

     Thread.Sleep(3000);   

     Guid g2 = Trace.CorrelationManager.ActivityId; 
     Debug.Assert(g1.Equals(g2)); 

     //This assert, LogicalOperation.Count, will eventually fail if there is a logical operation 
     //in effect when the Parallel.For operation was started. 
     Debug.Assert(Trace.CorrelationManager.LogicalOperationStack.Count == baseCount + 1, string.Format("MainThread = {0}, Thread = {1}, Count = {2}, ExpectedCount = {3}", mainThreadId, Thread.CurrentThread.ManagedThreadId, Trace.CorrelationManager.LogicalOperationStack.Count, baseCount + 1)); 
     Debug.Assert(Trace.CorrelationManager.LogicalOperationStack.Peek().Equals(lo1), string.Format("MainThread = {0}, Thread = {1}, Count = {2}, ExpectedCount = {3}", mainThreadId, Thread.CurrentThread.ManagedThreadId, Trace.CorrelationManager.LogicalOperationStack.Peek(), lo1)); 

     Trace.CorrelationManager.StopLogicalOperation(); 
    } 

    private static void UseTasks(bool encloseInLogicalOperation = false) 
    { 
     int totalThreads = 100; 
     TaskCreationOptions taskCreationOpt = TaskCreationOptions.None; 
     Task task = null; 
     Stopwatch stopwatch = new Stopwatch(); 
     stopwatch.Start(); 

     if (encloseInLogicalOperation) 
     { 
     Trace.CorrelationManager.StartLogicalOperation(); 
     } 

     Task[] allTasks = new Task[totalThreads]; 
     for (int i = 0; i < totalThreads; i++) 
     { 
     task = Task.Factory.StartNew(() => 
     { 
      DoLongRunningWork(encloseInLogicalOperation ? 1 : 0); 
     }, taskCreationOpt); 
     allTasks[i] = task; 
     } 
     Task.WaitAll(allTasks); 

     if (encloseInLogicalOperation) 
     { 
     Trace.CorrelationManager.StopLogicalOperation(); 
     } 

     stopwatch.Stop(); 
     Console.WriteLine(String.Format("Completed {0} tasks in {1} milliseconds", totalThreads, stopwatch.ElapsedMilliseconds)); 
     Console.WriteLine(String.Format("Used {0} threads", threadIds.Count)); 
     Console.WriteLine(String.Format("Main thread used in delegate {0} times", mainThreadUsedInDelegate)); 

     Console.ReadKey(); 
    } 

    private static void UseParallelFor(bool encloseInLogicalOperation = false) 
    { 
     int totalThreads = 100; 
     Stopwatch stopwatch = new Stopwatch(); 
     stopwatch.Start(); 

     if (encloseInLogicalOperation) 
     { 
     Trace.CorrelationManager.StartLogicalOperation(); 
     } 

     Parallel.For(0, totalThreads, i => 
     { 
     DoLongRunningWork(encloseInLogicalOperation ? 1 : 0); 
     }); 

     if (encloseInLogicalOperation) 
     { 
     Trace.CorrelationManager.StopLogicalOperation(); 
     } 

     stopwatch.Stop(); 
     Console.WriteLine(String.Format("Completed {0} tasks in {1} milliseconds", totalThreads, stopwatch.ElapsedMilliseconds)); 
     Console.WriteLine(String.Format("Used {0} threads", threadIds.Count)); 
     Console.WriteLine(String.Format("Main thread used in delegate {0} times", mainThreadUsedInDelegate)); 

     Console.ReadKey(); 
    } 

    } 
} 

की अगर LogicalOperationStack Parallel.For साथ इस्तेमाल किया जा सकता इस पूरे मुद्दे (और/या अन्य सूत्रण/टास्क निर्माण करती है) या कैसे यह शायद गुण अपने आप ही सवाल किया जा सकता है। शायद मैं एक प्रश्न पोस्ट करूंगा। इस बीच, मुझे आश्चर्य है कि क्या आपके पास इस पर कोई विचार है (या, मुझे आश्चर्य है कि आपने लॉजिकलऑपरेशनस्टैक का उपयोग करने पर विचार किया था क्योंकि गतिविधि आईडी सुरक्षित होने लगती है)।

[अंत चिपकाएं]

किसी को भी इस मुद्दे पर कोई विचार है?

+0

लॉजिकलऑपरेशनस्टैक और समांतर पर किसी भी विचार या विचार। के लिए? – wageoghe

+0

पूरी तरह से शोध के लिए धन्यवाद जो मुझे अपराधी को ढूंढने से बचाता है :) –

उत्तर

5

[अपडेट शुरू]

मैं भी Microsoft's Parallel Extensions for .Net support forum पर इस प्रश्न पूछा और अंततः एक answer from Stephen Toub प्राप्त किया। यह पता चला है कि bug in the LogicalCallContext है जो LogicalOperationStack को दूषित कर रहा है। एक अच्छा वर्णन भी है (एक जवाब में स्टीफन द्वारा एक फॉलोअप में जिसे मैंने उसके उत्तर में बनाया था) जो समानांतर कैसे करता है इसका संक्षिप्त विवरण देता है। कार्यों को हल करने के संबंध में काम करता है और यह समानांतर क्यों बनाता है। बग के लिए अतिसंवेदनशील।

नीचे दिए गए मेरे जवाब में मैं अनुमान लगाता हूं कि लॉजिकलऑपरेशनस्टैक समांतर के साथ संगत नहीं है। क्योंकि समानांतर। मुख्य थ्रेड का उपयोग "कार्यकर्ता" धागे में से एक के रूप में करता है। स्टीफन के स्पष्टीकरण के आधार पर, मेरी अटकलें गलत थीं। समांतर। मुख्य थ्रेड का उपयोग "कार्यकर्ता" धागे में से एक के रूप में करता है, लेकिन इसका उपयोग "जैसा है" नहीं किया जाता है। पहला कार्य मुख्य धागे पर चलाया जाता है, लेकिन इस तरह से चलाया जाता है कि ऐसा लगता है कि यह एक नए धागे पर चलाया जाता है। अधिक जानकारी के लिए स्टीफन का विवरण पढ़ें।

[समाप्ति अपडेट]

मैं क्या बता सकते हैं, इस सवाल का जवाब इस प्रकार है:

दोनों ActivityId और LogicalOperationStack CallContext.LogicalSetData के माध्यम से जमा हो जाती है। इसका मतलब है कि इन मानों को किसी भी "बच्चे" धागे में "प्रवाह" किया जाएगा। यह बहुत अच्छा है, जैसा कि आप कर सकते हैं, उदाहरण के लिए, एंट्रीआईड को एंट्री पॉइंट पर एक मल्टीथ्रेडेड सर्वर (एक सेवा कॉल कहें) में सेट करें और अंततः उस प्रविष्टि बिंदु से शुरू होने वाले सभी थ्रेड एक ही "गतिविधि" का हिस्सा हो सकते हैं। इसी प्रकार, लॉजिकल ऑपरेशंस (लॉजिकलऑपरेशनस्टैक के माध्यम से) बच्चे के थ्रेड पर भी बहती है।

साथ Trace.CorrelationManager.ActivityId के संबंध:

ActivityId सभी सूत्रण मॉडल है कि मैं इसके साथ परीक्षण किया है के साथ संगत प्रतीत हो रहा है:। धागे का उपयोग करते हुए सीधे, ThreadPool का उपयोग कर, टास्क का उपयोग, समानांतर का उपयोग कर *। सभी मामलों में, गतिविधि आईडी में अपेक्षित मूल्य है।

साथ Trace.CorrelationManager.LogicalOperationStack के संबंध:

LogicalOperationStack सबसे सूत्रण मॉडल के साथ संगत प्रतीत हो रहा है, लेकिन नहीं समानांतर के साथ *।। धागे का उपयोग सीधे, थ्रेडपूल, और कार्य, लॉजिकलऑपरेशनस्टैक (जैसा कि मेरे प्रश्न में दिए गए नमूना कोड में छेड़छाड़ किया गया है) इसकी अखंडता को बनाए रखता है। लॉजिकलऑपरेशनस्टैक की सामग्री हमेशा की अपेक्षा की जाती है।

लॉजिकलऑपरेशनस्टैक समांतर के साथ संगत नहीं है। के लिए। यदि एक तार्किक ऑपरेशन "प्रभाव में" है, तो यह है कि यदि आपने सहसंबंध प्रबंधक कहा है।समानांतर प्रारंभ करने से पहले StartLogicalOperation। * ऑपरेशन और फिर आप पैराले के संदर्भ में एक नया लॉजिकल ऑपरेशन शुरू करते हैं। * (यानी प्रतिनिधि में), तो LogicalOperationStack दूषित हो जाएगा। (मुझे कहना चाहिए कि यह संभवतः दूषित हो जाएगा। समानांतर। * कोई अतिरिक्त धागे नहीं बना सकता है, जिसका अर्थ है कि लॉजिकलऑपरेशनस्टैक सुरक्षित होगा)।

समस्या इस तथ्य से उत्पन्न होती है कि समांतर। * मुख्य थ्रेड का उपयोग करता है (या, शायद अधिक सही ढंग से, थ्रेड जो समानांतर ऑपरेशन शुरू करता है) अपने "कार्यकर्ता" धागे में से एक के रूप में। इसका मतलब है कि "कार्यकर्ता" धागे में "तार्किक परिचालन" शुरू और बंद कर दिया गया है जो "मुख्य" धागे जैसा ही है, "मुख्य" धागा का लॉजिकलऑपरेशनस्टैक संशोधित किया जा रहा है। भले ही कॉलिंग कोड (यानी प्रतिनिधि) ढेर को सही ढंग से बनाए रखता है (यह सुनिश्चित करना कि प्रत्येक स्टार्टलोगिकलऑपरेशन एक संबंधित स्टॉपलोगिकलऑपरेशन के साथ "रुक गया" है, "मुख्य" थ्रेड स्टैक संशोधित है। आखिरकार ऐसा लगता है (वैसे भी, मेरे लिए), "मुख्य" थ्रेड के लॉजिकलऑपरेशनस्टैक को अनिवार्य रूप से दो अलग-अलग "तार्किक" धागे द्वारा संशोधित किया जा रहा है: "मुख्य" धागा और "कार्यकर्ता" धागा, जो दोनों समान होते हैं धागा।

मुझे गहराई से पता नहीं है कि यह क्यों काम नहीं कर रहा है (कम से कम मैं इसे काम करने की अपेक्षा करता हूं)। मेरा सबसे अच्छा अनुमान यह है कि प्रत्येक बार जब प्रतिनिधि को थ्रेड पर निष्पादित किया जाता है (जो मुख्य धागे के समान नहीं होता है), थ्रेड मुख्य थ्रेड के लॉजिकलऑपरेशनस्टैक की वर्तमान स्थिति "विरासत" देता है। यदि प्रतिनिधि वर्तमान में मुख्य धागे (एक कार्यकर्ता थ्रेड के रूप में पुन: उपयोग किया जा रहा है) पर निष्पादित कर रहा है, और एक तार्किक ऑपरेशन शुरू कर दिया है, तो अन्य समांतर प्रतिनिधियों के एक (या एक से अधिक) मुख्य धागे के लॉजिकलऑपरेशनस्टैक को "उत्तराधिकारी" करेंगे प्रभाव में एक (या अधिक) नए तार्किक संचालन!

एफडब्ल्यूआईडब्ल्यू, मैंने लॉजिकलऑपरेशनस्टैक की नकल करने के लिए निम्नलिखित "लॉजिकल स्टैक" लागू किया है (मुख्य रूप से परीक्षण के लिए, मैं वास्तव में इसका उपयोग नहीं कर रहा हूं), लेकिन इसे इस तरह से करें कि यह समांतर के साथ काम करेगा। * इसे आज़माने और/या इसका उपयोग करने के लिए स्वतंत्र महसूस करें। परीक्षण करने के लिए,

LogicalOperation.OperationStack.Push()/Pop(). 


//OperationStack.cs 
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

using System.Runtime.Remoting.Messaging; 

namespace LogicalOperation 
{ 
    public static class OperationStack 
    { 
    private const string OperationStackSlot = "OperationStackSlot"; 

    public static IDisposable Push(string operation) 
    { 
     OperationStackItem parent = CallContext.LogicalGetData(OperationStackSlot) as OperationStackItem; 
     OperationStackItem op = new OperationStackItem(parent, operation); 
     CallContext.LogicalSetData(OperationStackSlot, op); 
     return op; 
    } 

    public static object Pop() 
    { 
     OperationStackItem current = CallContext.LogicalGetData(OperationStackSlot) as OperationStackItem; 

     if (current != null) 
     { 
     CallContext.LogicalSetData(OperationStackSlot, current.Parent); 
     return current.Operation; 
     } 
     else 
     { 
     CallContext.FreeNamedDataSlot(OperationStackSlot); 
     } 
     return null; 
    } 

    public static object Peek() 
    { 
     OperationStackItem top = Top(); 
     return top != null ? top.Operation : null; 
    } 

    internal static OperationStackItem Top() 
    { 
     OperationStackItem top = CallContext.LogicalGetData(OperationStackSlot) as OperationStackItem; 
     return top; 
    } 

    public static IEnumerable<object> Operations() 
    { 
     OperationStackItem current = Top(); 
     while (current != null) 
     { 
     yield return current.Operation; 
     current = current.Parent; 
     } 
    } 

    public static int Count 
    { 
     get 
     { 
     OperationStackItem top = Top(); 
     return top == null ? 0 : top.Depth; 
     } 
    } 

    public static IEnumerable<string> OperationStrings() 
    { 
     foreach (object o in Operations()) 
     { 
     yield return o.ToString(); 
     } 
    } 
    } 
} 


//OperationStackItem.cs 
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace LogicalOperation 
{ 
    public class OperationStackItem : IDisposable 
    { 
    private OperationStackItem parent = null; 
    private object operation; 
    private int depth; 
    private bool disposed = false; 

    internal OperationStackItem(OperationStackItem parentOperation, object operation) 
    { 
     parent = parentOperation; 
     this.operation = operation; 
     depth = parent == null ? 1 : parent.Depth + 1; 
    } 

    internal object Operation { get { return operation; } } 
    internal int Depth { get { return depth; } } 

    internal OperationStackItem Parent { get { return parent; } } 

    public override string ToString() 
    { 
     return operation != null ? operation.ToString() : ""; 
    } 

    #region IDisposable Members 

    public void Dispose() 
    { 
     if (disposed) return; 

     OperationStack.Pop(); 

     disposed = true; 
    } 

    #endregion 
    } 
} 

को

Trace.CorrelationManager.StartLogicalOperation/StopLogicalOperation 
कॉल के साथ अपने मूल प्रश्न से नमूना कोड में

के लिए कॉल की जगह यह गुंजाइश वस्तुओं ब्रेंट VanderMeide यहाँ द्वारा वर्णित से प्रेरित था: http://www.dnrtv.com/default.aspx?showNum=114

आप इस वर्ग का उपयोग इस तरह कर सकते हैं:

public void MyFunc() 
{ 
    using (LogicalOperation.OperationStack.Push("MyFunc")) 
    { 
    MyOtherFunc(); 
    } 
} 

public void MyOtherFunc() 
{ 
    using (LogicalOperation.OperationStack.Push("MyOtherFunc")) 
    { 
    MyFinalFunc(); 
    } 
} 

public void MyFinalFunc() 
{ 
    using (LogicalOperation.OperationStack.Push("MyFinalFunc")) 
    { 
    Console.WriteLine("Hello"); 
    } 
} 
2

मैं जांच कर रहा था एक लॉजिकल-स्टैक रखने का एक तरीका जिस पर टीपीएल का भारी उपयोग करने वाले एप्लिकेशन में आसानी से काम करना चाहिए। मैंने LogicalOperationStack का उपयोग करने का निर्णय लिया क्योंकि यह मौजूदा कोड को बदलने के बिना आवश्यक सभी चीजें करता था। लेकिन तब मैं LogicalCallContext में एक बग के बारे में पढ़ा:

https://connect.microsoft.com/VisualStudio/feedback/details/609929/logicalcallcontext-clone-bug-when-correlationmanager-slot-is-present

तो मैं इस बग के लिए एक समाधान खोजने के लिए कोशिश की और मुझे लगता है कि मैं इसे TPL (धन्यवाद ILSpy) के लिए काम मिल गया:

public static class FixLogicalOperationStackBug 
{ 
    private static bool _fixed = false; 

    public static void Fix() 
    { 
     if (!_fixed) 
     { 
      _fixed = true; 

      Type taskType = typeof(Task); 
      var s_ecCallbackField = taskType.GetFields(BindingFlags.Static | BindingFlags.NonPublic).First(f => f.Name == "s_ecCallback"); 
      ContextCallback s_ecCallback = (ContextCallback)s_ecCallbackField.GetValue(null); 

      ContextCallback injectedCallback = new ContextCallback(obj => 
      { 
       // Next line will set the private field m_IsCorrelationMgr of LogicalCallContext which isn't cloned 
       CallContext.LogicalSetData("System.Diagnostics.Trace.CorrelationManagerSlot", Trace.CorrelationManager.LogicalOperationStack); 
       s_ecCallback(obj); 
      }); 

      s_ecCallbackField.SetValue(null, injectedCallback); 
     } 
    } 
} 
+0

कोई भी मौका आप बग को ठीक करने के लिए इस विधि को कहां और कब कॉल कर सकते हैं के बारे में विवरण जोड़ सकते हैं? – jpierson

+0

जब मैं इस फिक्स को आज़माता हूं और इसे आमंत्रित करने से पहले s_ecCallback के आसपास एक नल चेक जोड़ता हूं तो मुझे एक NullReferenceException मिलता है, बस फिक्स() को कॉल करने के बाद मेरे टेस्ट कोड को फ्रीज करने का कारण बनता है। – jpierson

+0

@jpierson: यदि आप कॉलबैक में शून्य जांच करते हैं, तो आप कार्य को वास्तव में चलने से रोक सकते हैं। यदि कोई शून्य जांच होनी चाहिए, तो s_ecCallback के असाइनमेंट के बाद यह सही होगा।लेकिन मुझे लगता है कि फिक्स करने के लिए आपका कॉल बहुत जल्दी है: फ्रेमवर्क को इसे असाइन करने में बदलाव होने से पहले भी। –

संबंधित मुद्दे