2011-11-23 18 views
87

के रूप में उपयोग नहीं किया जा सकता है, मैं अपने व्यूमोडेल में लंबे समय तक चलने वाले सर्वर कॉल चलाने के लिए Tasks का उपयोग कर रहा हूं और परिणाम Dispatcher पर TaskScheduler.FromSyncronizationContext() का उपयोग करके वापस कर दिए गए हैं। उदाहरण के लिए:वर्तमान सिंक्रनाइज़ेशन कॉन्टेक्स्ट को टास्कशेड्यूलर

var context = TaskScheduler.FromCurrentSynchronizationContext(); 
this.Message = "Loading..."; 
Task task = Task.Factory.StartNew(() => { ... }) 
      .ContinueWith(x => this.Message = "Completed" 
          , context); 

जब मैं एप्लिकेशन निष्पादित करता हूं तो यह ठीक काम करता है। लेकिन मैं FromCurrentSynchronizationContext के रूप में करने के लिए फोन पर त्रुटि संदेश मिलता है जब मैं Resharper पर मेरे NUnit परीक्षण चलाने:

वर्तमान SynchronizationContext एक TaskScheduler के रूप में इस्तेमाल नहीं किया जा सकता।

मुझे लगता है कि ऐसा इसलिए है क्योंकि परीक्षण कार्यकर्ता धागे पर चल रहे हैं। मैं कैसे सुनिश्चित कर सकता हूं कि परीक्षण मुख्य धागे पर चल रहे हैं? अन्य सुझावों का स्वागत है।

उत्तर

133

आपको सिंक्रनाइज़ेशन कॉन्टेक्स्ट प्रदान करने की आवश्यकता है। इस प्रकार मैं इसे संभालता हूं:

[SetUp] 
public void TestSetUp() 
{ 
    SynchronizationContext.SetSynchronizationContext(new SynchronizationContext()); 
} 
+0

काम किया, धन्यवाद! – anivas

+5

एमएसटीएस्ट के लिए: क्लासInitializeAttribute के साथ चिह्नित विधि में ऊपर कोड डालें। – SACO

+0

जानना अच्छा है ... –

11

रिच मेलटन का समाधान मेरे लिए काम नहीं करता था। ऐसा इसलिए है क्योंकि मेरा TestInitialize फ़ंक्शन एसिंक है, जैसा कि मेरे परीक्षण हैं, इसलिए प्रत्येक await वर्तमान SynchronizationContext खो गया है। ऐसा इसलिए है क्योंकि एमएसडीएन बताते हैं, SynchronizationContext वर्ग "गूंगा" है और केवल थ्रेड पूल में सभी काम कतार है।

क्या मेरे लिए काम किया वास्तव में सिर्फ FromCurrentSynchronizationContext कॉल पर लंघन जब वहाँ नहीं है एक SynchronizationContext (है कि, अगर वर्तमान संदर्भ अशक्त है)। यदि कोई यूआई थ्रेड नहीं है, तो मुझे इसके साथ पहली बार सिंक्रनाइज़ करने की आवश्यकता नहीं है।

TaskScheduler syncContextScheduler; 
if (SynchronizationContext.Current != null) 
{ 
    syncContextScheduler = TaskScheduler.FromCurrentSynchronizationContext(); 
} 
else 
{ 
    // If there is no SyncContext for this thread (e.g. we are in a unit test 
    // or console scenario instead of running in an app), then just use the 
    // default scheduler because there is no UI thread to sync with. 
    syncContextScheduler = TaskScheduler.Current; 
} 

मैं इस समाधान के विकल्प की तुलना में अधिक स्पष्ट है, जो कहाँ पाया:

  • ViewModel करने के लिए एक TaskScheduler दर्रा (निर्भरता इंजेक्शन के माध्यम से)
  • एक परीक्षण SynchronizationContext और एक "नकली" यूआई बनाएं परीक्षण के लिए धागा - मेरे लिए और अधिक परेशानी है कि यह

मैं कुछ थ्रेडिंग न्युअंस खो देता हूं, लेकिन मैं नहीं हूं स्पष्ट रूप से परीक्षण करना कि मेरे OnPropertyChanged कॉलबैक एक विशिष्ट धागे पर ट्रिगर करते हैं, इसलिए मैं इसके साथ ठीक हूं। new SynchronizationContext() का उपयोग करने वाले अन्य उत्तरों वैसे भी उस लक्ष्य के लिए वास्तव में बेहतर नहीं करते हैं।

+0

आपका 'else' केस विंडोज़ सेवा ऐप में भी असफल हो जाएगा, जिसके परिणामस्वरूप' syncContextScheduler == null' – FindOutIslamNow

0

मैं SynchronizationContext काम करने के लिए गारंटी है करने के लिए कई समाधान संयुक्त है

using System; 
using System.Threading; 
using System.Threading.Tasks; 

public class CustomSynchronizationContext : SynchronizationContext 
{ 
    public override void Post(SendOrPostCallback action, object state) 
    { 
     SendOrPostCallback actionWrap = (object state2) => 
     { 
      SynchronizationContext.SetSynchronizationContext(new CustomSynchronizationContext()); 
      action.Invoke(state2); 
     }; 
     var callback = new WaitCallback(actionWrap.Invoke); 
     ThreadPool.QueueUserWorkItem(callback, state); 
    } 
    public override SynchronizationContext CreateCopy() 
    { 
     return new CustomSynchronizationContext(); 
    } 
    public override void Send(SendOrPostCallback d, object state) 
    { 
     base.Send(d, state); 
    } 
    public override void OperationStarted() 
    { 
     base.OperationStarted(); 
    } 
    public override void OperationCompleted() 
    { 
     base.OperationCompleted(); 
    } 

    public static TaskScheduler GetSynchronizationContext() { 
     TaskScheduler taskScheduler = null; 

     try 
     { 
     taskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); 
     } catch {} 

     if (taskScheduler == null) { 
     try 
     { 
      taskScheduler = TaskScheduler.Current; 
     } catch {} 
     } 

     if (taskScheduler == null) { 
     try 
     { 
      var context = new CustomSynchronizationContext(); 
      SynchronizationContext.SetSynchronizationContext(context); 
      taskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); 
     } catch {} 
     } 

     return taskScheduler; 
    } 
} 

उपयोग:

var context = CustomSynchronizationContext.GetSynchronizationContext(); 

if (context != null) 
{ 
    Task.Factory 
     .StartNew(() => { ... }) 
     .ContinueWith(x => { ... }, context); 
} 
else 
{ 
    Task.Factory 
     .StartNew(() => { ... }) 
     .ContinueWith(x => { ... }); 
}