वर्तमान सिंक्रोनाइज़ेशनकोटेक्स्ट का उपयोग टास्कस्क्रिडर के रूप में नहीं किया जा सकता है


98

मैं अपने ViewModel में लंबे समय से चल रहे सर्वर कॉल को चलाने के लिए कार्य का उपयोग कर रहा हूं और परिणाम Dispatcherका उपयोग करने पर वापस भेज दिया गया है TaskScheduler.FromSyncronizationContext()। उदाहरण के लिए:

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

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

वर्तमान सिंक्रोनाइज़ेशनकोटेक्स्ट का उपयोग टास्कस्क्रिडर के रूप में नहीं किया जा सकता है।

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


मेरे मामले में मैं TaskScheduler.FromCurrentSynchronizationContext()एक लंबोदर के अंदर का उपयोग कर रहा था और निष्पादन दूसरे धागे के लिए स्थगित कर दिया गया था। लंबोदर के बाहर संदर्भ मिलने से समस्या ठीक हो गई।
एमकाज़म अख़गरी

जवाबों:


145

आपको एक SynchronizationContext प्रदान करना होगा। इस तरह से मैं इसे संभाल रहा हूँ:

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

6
MSTest के लिए: ClassInitializeAttribute के साथ चिह्नित विधि में ऊपर कोड डालें।
डैनियल बायसर

6
@ एसएसीओ: वास्तव में, मुझे इसे एक विधि के साथ रखना होगा TestInitializeAttribute, अन्यथा केवल पहला टेस्ट पास करना होगा।
थोरिन

2
Xunit परीक्षणों के लिए, मैंने इसे स्थैतिक प्रकार के ctor में रखा, क्योंकि इसे केवल एक बार तय करने की आवश्यकता है।
कोडकाइजन

3
मुझे समझ नहीं आ रहा है कि इस जवाब को समाधान के रूप में क्यों स्वीकार किया गया। यह काम नहीं करता। और कारण सरल है: SynchronizationContext एक डमी क्लास है जिसका सेंड / पोस्ट फंक्शन बेकार है। इस वर्ग को एक ठोस वर्ग के बजाय अमूर्त होना चाहिए जो संभवतः लोगों को "यह काम कर रहा है" के एक गलत अर्थ में ले जाता है। @tofutim आप शायद SyncContext से प्राप्त अपना स्वयं का कार्यान्वयन प्रदान करना चाहते हैं।
h9uest

1
मुझे लगता है कि मैंने हल निकाल लिया। मेरा TestInitialize अतुल्यकालिक है। हर बार जब TestInit में "प्रतीक्षा" होती है, तो वर्तमान सिंक्रोनाइज़ेशन कॉन्टेक्स्ट खो जाता है। इसका कारण यह है (जैसा कि @ h9uest ने बताया है), SynchronizationContext का डिफ़ॉल्ट कार्यान्वयन थ्रेडपूल के कार्यों को कतार में रखता है और वास्तव में एक ही थ्रेड पर जारी नहीं रहता है।
सपन

24

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

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

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

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

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

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


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

एक ही समस्या के पार आया, लेकिन इसके बजाय मैंने NUnit स्रोत कोड पढ़ा। यदि यह एक STA थ्रेड में चल रहा है, तो केवल AsyncToSyncAdapter आपके SynchronizationContext को ओवरराइड करता है। वर्कअराउंड अपनी कक्षा को एक [RequiresThread]विशेषता के साथ चिह्नित करना है ।
एरन

1

मैंने सिंक्रोनाइज़ेशन के लिए गारंटी के लिए कई समाधानों को संयोजित किया है।

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 => { ... });
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.