2012-11-26 17 views
6

के साथ एसिंक जाने पर HttpContext को संरक्षित करें मैं एएसपी.Net होस्टेड वेबएपीआई सेवाओं का एक सेट बना रहा हूं जो पुराने पुस्तकालय का उपयोग करना चाहिए जो HttpContext.Current पर निर्भर करता है। मुझे यह सुनिश्चित करने में परेशानी हो रही है कि एसिंक कॉल में भाग लेने वाली सभी विधियों में संदर्भ संरक्षित है। मैंने नीचे दिए गए कोड पर प्रतीक्षा/कार्य के साथ कई भिन्नताओं की कोशिश की है। प्रतीक्षा करें और टास्कशेड्यूलर। FromCurrentSynchronizationContext()।वेबएपीआई (मध्यम ट्रस्ट)

[HttpGet] 
    public Task<IEnumerable<string>> ContinueWith() 
    { 
     Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR"); //or another culture that is not the default on your machine 
     Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture; 

     var output = new List<string> { TestOutput("Action start") }; 

     var task = Task.Factory.StartNew(() => 
      { 
       Thread.Sleep(1000); 
       return TestOutput("In Task"); 
      }).ContinueWith(slowString => 
      { 
       output.Add(slowString.Result); 

       output.Add(TestOutput("Action end")); 
       return output as IEnumerable<string>; 
      }); 

     output.Add(TestOutput("Action Mid")); 

     return task; 
    } 

    private string TestOutput(string label) 
    { 
     var s = label + " ThreadID: " + Thread.CurrentThread.ManagedThreadId.ToString(CultureInfo.InvariantCulture); 
     s += " " + Thread.CurrentThread.CurrentCulture.EnglishName; 
     s += HttpContext.Current == null ? " No Context" : " Has Context"; 
     Debug.WriteLine(s); 
     return s; 
    } 

मैं चाहते हैं यह सुनिश्चित करें कि CurrentCulture fr-एफआर है सक्षम होने के लिए, और HttpContext.Current कि प्रत्येक बिंदु जहां TestOutput कहा जाता है पर अशक्त नहीं है। मैंने ऐसा कुछ भी करने के लिए "टास्क" कॉल के लिए ऐसा करने में सफल नहीं किया है। इसके अलावा मेरी कुछ टेस्ट थ्रेड आईडी में कभी भी यह सुझाव नहीं दिया जाता है कि मैंने विधि की एसिंक्रोनिटी को प्रभावी ढंग से हटा दिया है। मैं कैसे सुनिश्चित कर सकता हूं कि संस्कृति और HttpContext.Current टेस्टऑटपुट पर प्रत्येक कॉल पर संरक्षित हैं, और यह कोड अलग-अलग धागे पर चलाने के लिए स्वतंत्र है?

एक बंद करने में HttpContext.Current को कैप्चर करना और फिर इसे फिर से सेट करना मेरे लिए काम नहीं करेगा क्योंकि मुझे मध्यम ट्रस्ट का समर्थन करने की आवश्यकता है जो HttpContext.Current Setter को कॉल करते समय सुरक्षा अपवाद फेंक देगा।

उत्तर

6

जब भी आप await कार्य करते हैं तो संदर्भ संरक्षित किया जाता है।

आप क्या देख रहे हैं थ्रेड पूल कार्यों के लिए कोई संदर्भ नहीं है वह यह है कि (Task.Run, TaskFactory.StartNew, या कहें कि BackgroundWorker या Thread या Delegate.BeginInvoke के लिए)। यह सामान्य और उम्मीद है।

तो, थ्रेड पूल कार्य का उपयोग न करें। आपका उदाहरण कोड HttpContext वाले एकाधिक धागे के साथ समांतर प्रसंस्करण करना चाहता है, जो बस संभव नहीं है।

यदि आप चाहें तो समवर्ती async तरीकों से कर सकते हैं, लेकिन यह है कि आपके Thread.Sleep वास्तव में हो सकता है एक सीपीयू आधारित पद्धति के स्थान पर एक async विधि की आवश्यकता है:

[HttpGet] 
public async Task<IEnumerable<string>> Test() 
{ 
    Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR"); 
    Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture; 

    var output = new List<string> { TestOutput("Action start") }; 

    var task = SlowStringAsync(); 
    output.Add(TestOutput("Action Mid")); 
    output.Add(await task); 
    output.Add(TestOutput("Action end")); 
    return output; 
} 

public async Task<string> SlowStringAsync() 
{ 
    await Task.Delay(1000); 
    return TestOutput("In Task"); 
} 

अपने पुराने पुस्तकालय अपने नियंत्रण से बाहर है तो और आप इसे async नहीं बना सकते हैं, तो आपको इसे समकालिक रूप से कॉल करना होगा। यह इस तरह की परिस्थितियों में एक async विधि से एक तुल्यकालिक विधि कॉल करने के स्वीकार्य है:

[HttpGet] 
public async Task<IEnumerable<string>> Test() 
{ 
    Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR"); 
    Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture; 

    var output = new List<string> { TestOutput("Action start") }; 

    output.Add(TestOutput("Action Mid")); 
    Thread.Sleep(1000); 
    output.Add(TestOutput("Not Really In Task")); 
    output.Add(TestOutput("Action end")); 
    return output; 
} 
+0

यह मेरी समझ थी कि AspNetSynchronizationContext विशेष रूप से अन्य थ्रेड पर निष्पादित होने पर HttpContext जैसी चीजों को संरक्षित करने के लिए है। क्या मैं गलतफहमी कर रहा हूं, या AspNetSynchronizationContext बस इस स्थिति में लागू नहीं है? – ScottS

+0

'AspNetSynchronizationContext'' HttpContext' को संरक्षित करता है, लेकिन एक समय में केवल एक थ्रेड अनुरोध संदर्भ में हो सकता है। आप संदर्भ को अन्य धागे पर नहीं डाल सकते हैं, लेकिन 'AspNetSynchronizationContext' थ्रेड पूल थ्रेड पर संदर्भ डालने का प्रभारी है जो 'async' विधि को फिर से शुरू करने के बाद फिर से शुरू करता है। –

+0

@StephenCleary - मेरी अज्ञानता को क्षमा करें, लेकिन 'Task.Factory.StartNew()' विधि का उपयोग करते हुए 'CurrentCulture' को धागे में संरक्षित किया जाएगा - उदा। 'वापसी का इंतजार टास्क.फैक्टरी.स्टार्टन्यू (() => {doSomething();};' –

7

एक छोटी सी ध्यान तथ्य, HttpContext.Current लिखने योग्य है।

var context = HttpContext.Current; 
var task = Task.Factory.StartNew(() => { 
    HttpContext.Current = context; 
    // You may want to set CultureInformation here too. 

    return TestOutput("In Task"); 
}); 
+0

धन्यवाद साइमन। यह एक छोटा ज्ञात तथ्य है, हालांकि मध्यम ट्रस्ट में चलते समय यह सुरक्षा अपवाद का कारण बन जाएगा, जिसे मुझे प्रश्न में नोटिस के रूप में समर्थन देने की आवश्यकता है। – ScottS

+0

यह भी बेहद खतरनाक है क्योंकि आपके पास एक ही 'HttpContext.Current' के साथ कई धागे हैं, जो विशेष रूप से * नहीं * मल्टीथ्रेडेड एक्सेस के लिए डिज़ाइन किए गए हैं। –

+2

मुझे समझ में आता है कि यह सबसे अच्छा तरीका नहीं है, लेकिन थ्रेडिंग मुद्दों के कारण खतरनाक होने के संबंध में, इसके चारों ओर एक ताला लगाएगा? लॉक के दौरान क्लोजर वेरिएबल को आवंटित किया जाएगा, या जब तक यह पढ़ा जाएगा तब तक इंतजार होगा? –