दिलचस्प पोस्ट
एक अलग फाइल में प्रत्येक सार्वजनिक वर्ग क्यों है? अपडेट की हुई पंक्ति में डेटाफ्रेम पंक्ति को मैप करने का प्रयास करते समय एन्कोडर त्रुटि ओपनजीएल पर मल्टीथ्रेडेड रेन्डरिंग क्या सूची <T> के संग्रह के आकार को प्रारंभ करने के लिए उपयुक्त है यदि यह आकार उचित रूप से ज्ञात है? C ++ में स्ट्रिंग से कुछ वर्ण कैसे निकालें? IPhone पर ऐप को डिबग करने का प्रयास करते हुए "एक हस्ताक्षरित संसाधन जोड़ा गया, संशोधित या हटा दिया गया" संदेश प्राप्त करें कई मेवेन परियोजनाओं के बीच साझा गुण कैसे साझा करें? विदेशी कुंजी बाधाओं का कारण हो सकता है चक्र या कई कैस्केड पथ? सूत्रों के बीच क्या संसाधन साझा किए जाते हैं? पायथन अनुरोध अनुरोध। Exceptions.SSLError: _ssl.c: 504: प्रोटोकॉल का उल्लंघन हुआ है EOF एसक्यूएल (माय एसक्यूएल) बनाम नोएसक्यूएल (सीचडीबी) डेटाआरडियर और ओएलईडीबी जेट डेटा प्रदाता का उपयोग करते हुए एक सीएसवी फाइल पढ़ने पर, मैं कॉलम डेटा प्रकारों को कैसे नियंत्रित कर सकता हूं? किसी स्विंग विंडो के सापेक्ष किसी माउस क्लिक का स्थान कैसे प्राप्त करें एनजी-क्लास द्वारा निर्धारित क्लास के आधार पर मैं एक AngularJS निर्देश कैसे लागू करूं? एकाधिक @ कंट्रोलर ऐडवाइस @ एक्सेप्शनहैंडलर्स की प्राथमिकता निर्धारित करना

StaTaskScheduler और एसटीए धागा संदेश पंपिंग

TL; DR: StaTaskScheduler द्वारा चलाए गए कार्य के अंदर एक गतिरोध। दीर्घ संस्करण:

मैं समांतर टीम द्वारा समानांतर StaTaskScheduler से StaTaskScheduler का उपयोग कर रहा हूं, जो किसी तृतीय पक्ष द्वारा प्रदान की जाने वाली कुछ विरासत STA COM ऑब्जेक्ट्स होस्ट करता है। StaTaskScheduler कार्यान्वयन के विवरण का विवरण निम्नलिखित कहते हैं:

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

मैंने सोचा कि टीपीएल के ब्लॉकिंग पार्ट्स का मतलब एक प्रतीक्षा एपीआई का उपयोग करेगा जो CoWaitForMultipleHandles थ्रेड पर कॉल किए जाने पर डेडलॉक परिस्थितियों से बचने के लिए CoWaitForMultipleHandles जैसे संदेश पंप करता है।

मेरी स्थिति में, मेरा मानना ​​है कि निम्नलिखित हो रहा है: इन-कॉस्ट एसटीए कॉम ऑब्जेक्ट ए ने कॉल आउट आउट की प्रि ऑब्जेक्ट बी को कॉल किया है, फिर आउटगोइंग कॉल के एक हिस्से के रूप में बी से कॉलबैक की उम्मीद है।

एक सरल रूप में:

 var result = await Task.Factory.StartNew(() => { // in-proc object A var a = new A(); // out-of-proc object B var b = new B(); // A calls B and B calls back A during the Method call return a.Method(b); }, CancellationToken.None, TaskCreationOptions.None, staTaskScheduler); 

समस्या यह है, a.Method(b) कभी रिटर्न नहीं देता है जहां तक ​​मैं बता सकता हूं, ऐसा इसलिए होता है क्योंकि ब्लॉकिंग BlockingCollection<Task> अंदर किसी अवरुद्ध प्रतीक्षा संदेश पंप नहीं करता, इसलिए उद्धृत वक्तव्य के बारे में मेरी धारणा शायद गलत है

संपादित करें एक ही कोड काम करता है जब WinForms परीक्षण के UI थ्रेड पर क्रियान्वित किया जाता है (जो कि Task.Factory.StartNewstaTaskScheduler को Task.Factory.StartNew बजाय staTaskScheduler प्रदान Task.Factory.StartNew )।

इसका समाधान करने का सही तरीका क्या है? क्या मुझे एक कस्टम सिंक्रनाइज़ेशन प्रसंग लागू किया जाना चाहिए, जो स्पष्ट रूप से CoWaitForMultipleHandles साथ संदेशों को पंप करेगा, और इसे StaTaskScheduler द्वारा शुरू किए गए प्रत्येक एसटीए थ्रेड पर इंस्टॉल करें?

यदि हां, तो क्या BlockingCollection का अंतर्निहित कार्यान्वयन मेरे SynchronizationContext.Wait कंसोर्ट। SynchronizationContext.Wait विधि को बुला सकता है? क्या मैं SynchronizationContext.WaitHelper को कार्यान्वित करने के लिए SynchronizationContext.WaitSynchronizationContext.Wait SynchronizationContext.WaitHelper का उपयोग कर सकता हूं?


कुछ कोड दिखाते हुए दिखाया गया है कि एक अवरुद्ध प्रतीक्षा करते समय एक प्रबंधित एसटीए धागा पंप नहीं करता है। कोड कॉपी / पेस्ट / रन के लिए तैयार एक पूर्ण कंसोल ऐप है:

 using System; using System.Collections.Concurrent; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; namespace ConsoleTestApp { class Program { // start and run an STA thread static void RunStaThread(bool pump) { // test a blocking wait with BlockingCollection.Take var tasks = new BlockingCollection<Task>(); var thread = new Thread(() => { // Create a simple Win32 window var hwndStatic = NativeMethods.CreateWindowEx(0, "Static", String.Empty, NativeMethods.WS_POPUP, 0, 0, 0, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); // subclass it with a custom WndProc IntPtr prevWndProc = IntPtr.Zero; var newWndProc = new NativeMethods.WndProc((hwnd, msg, wParam, lParam) => { if (msg == NativeMethods.WM_TEST) Console.WriteLine("WM_TEST processed"); return NativeMethods.CallWindowProc(prevWndProc, hwnd, msg, wParam, lParam); }); prevWndProc = NativeMethods.SetWindowLong(hwndStatic, NativeMethods.GWL_WNDPROC, newWndProc); if (prevWndProc == IntPtr.Zero) throw new ApplicationException(); // post a test WM_TEST message to it NativeMethods.PostMessage(hwndStatic, NativeMethods.WM_TEST, IntPtr.Zero, IntPtr.Zero); // BlockingCollection blocks without pumping, NativeMethods.WM_TEST never arrives try { var task = tasks.Take(); } catch (Exception e) { Console.WriteLine(e.Message); } if (pump) { // NativeMethods.WM_TEST will arrive, because Win32 MessageBox pumps Console.WriteLine("Now start pumping..."); NativeMethods.MessageBox(IntPtr.Zero, "Pumping messages, press OK to stop...", String.Empty, 0); } }); thread.SetApartmentState(ApartmentState.STA); thread.Start(); Thread.Sleep(2000); // this causes the STA thread to end tasks.CompleteAdding(); thread.Join(); } static void Main(string[] args) { Console.WriteLine("Testing without pumping..."); RunStaThread(false); Console.WriteLine("\nTest with pumping..."); RunStaThread(true); Console.WriteLine("Press Enter to exit"); Console.ReadLine(); } } // Interop static class NativeMethods { [DllImport("user32")] public static extern IntPtr SetWindowLong(IntPtr hwnd, int nIndex, WndProc newProc); [DllImport("user32")] public static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hwnd, int msg, int wParam, int lParam); [DllImport("user32.dll")] public static extern IntPtr CreateWindowEx(int dwExStyle, string lpClassName, string lpWindowName, int dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam); [DllImport("user32.dll")] public static extern bool PostMessage(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll")] public static extern int MessageBox(IntPtr hwnd, string text, String caption, int options); public delegate IntPtr WndProc(IntPtr hwnd, int msg, int wParam, int lParam); public const int GWL_WNDPROC = -4; public const int WS_POPUP = unchecked((int)0x80000000); public const int WM_USER = 0x0400; public const int WM_TEST = WM_USER + 1; } } 

यह उत्पादन का उत्पादन करता है:

 बिना पंपिंग का परीक्षण ...
 संग्रह तर्क खाली है और अतिरिक्त के संबंध में इसे पूर्ण रूप से चिह्नित किया गया है।

 पंपिंग के साथ टेस्ट करें ...
 संग्रह तर्क खाली है और अतिरिक्त के संबंध में इसे पूर्ण रूप से चिह्नित किया गया है।
 अब पम्पिंग शुरू करें ...
 WM_TEST संसाधित
 बाहर निकलने के लिए Enter दबाएं

Solutions Collecting From Web of "StaTaskScheduler और एसटीए धागा संदेश पंपिंग"

आपकी समस्या की मेरी समझ: आप अपने विरासत COM वस्तुओं के लिए क्लासिक COM STA अपार्टमेंट को व्यवस्थित करने के लिए केवल StaTaskScheduler का उपयोग कर रहे हैं। आप StaTaskScheduler के STA थ्रेड पर WinForms या WPF कोर संदेश लूप नहीं चला रहे हैं। यही है, आप Application.Run तरह कुछ भी नहीं उपयोग कर रहे हैं। Dispatcher.PushFrame , Application.DoEventsDispatcher.PushFrame या Dispatcher.PushFrame .उस धागे के अंदर पुशफ्रेम। मुझे सही करें अगर यह गलत धारणा है

स्वयं द्वारा, StaTaskScheduler एसटीए थ्रेड्स पर किसी भी सिंक्रनाइज़ेशन प्रसंग को स्थापित नहीं करता है। इस प्रकार, आप अपने लिए संदेश पंप करने के लिए CLR पर भरोसा कर रहे हैं। मुझे सिर्फ सीएनआरआर में एसआरए थ्रेड्स पर सीएलआर पंप, अपार्टमेंट में फ्लैट और सीआरआर में पंम ब्रममे द्वारा एक निहित पुष्टि मिली है:

मैं यह कहता हूं कि एसटीए थ्रेड पर कॉल किए जाने पर प्रबंधित ब्लॉकिंग "कुछ पम्पिंग" प्रदर्शन करेगा। क्या यह जानना बहुत अच्छा नहीं होगा कि क्या पंप मिलेगा? दुर्भाग्य से, पम्पिंग एक काली कला है जो नश्वर समझ से परे है। Win2000 और ऊपर, हम केवल OLE32 के CoWaitForMultipleHandles सेवा को प्रतिनिधि करते हैं।

यह दर्शाता है कि CLR आंतरिक रूप से CoWaitForMultipleHandles धागे के लिए CoWaitForMultipleHandles का उपयोग करता है। इसके अलावा, COWAIT_DISPATCH_WINDOW_MESSAGES ध्वज के लिए MSDN डॉक्स का उल्लेख इस प्रकार है :

… एसटीए में केवल विशेष-सीज़ित संदेशों का एक छोटा सेट प्रेषित है।

मैंने उस पर कुछ शोध किया , लेकिन आपके नमूना कोड से WM_TEST को WM_TEST के साथ पंप नहीं किया जा सका, हमने चर्चा की है कि आपके प्रश्नों पर टिप्पणी में मेरी समझ है, विशेष-सीज़ित संदेशों का उल्लिखित छोटा समूह वास्तव में कुछ COM marshaller- विशिष्ट संदेश तक सीमित है, और आपके WM_TEST जैसे किसी भी सामान्य सामान्य प्रयोजन संदेश को शामिल नहीं करता है

तो, अपने प्रश्न का उत्तर देने के लिए:

… क्या मुझे एक कस्टम सिंक्रनाइज़ेशन प्रसंग को लागू किया जाना चाहिए, जो स्पष्ट रूप से कोआइएफ़एफ़एफ़फ़ोरमल्टिप्लिकहैंड्स के साथ संदेश पंप करेगा, और इसे स्टाटेसशेड्यूलर द्वारा शुरू किए गए प्रत्येक एसटीए थ्रेड पर इंस्टॉल करना चाहिए?

हां, मेरा मानना ​​है कि कस्टम सिंक्रनाइज़ेशन प्रसंग और ओपरराइड SynchronizationContext.Wait कॉन्टैक्स। SynchronizationContext.Wait वास्तव में सही समाधान है।

हालांकि, आपको CoWaitForMultipleHandles का उपयोग करने से बचना चाहिए, और इसके बजाय MsgWaitForMultipleObjectsEx उपयोग करें यदि MsgWaitForMultipleObjectsEx इंगित करता है कि कतार में एक लंबित संदेश है, तो आपको इसे PeekMessage(PM_REMOVE) और DispatchMessage साथ मैन्युअल रूप से पंप करना चाहिए। फिर आपको हैंडल के लिए इंतजार करना जारी रखना चाहिए, सभी एक ही SynchronizationContext.Wait कॉन्टैक्स। प्रतीक्षा कॉल के अंदर।

ध्यान दें कि MsgWaitForMultipleObjectsEx और MsgWaitForMultipleObjects बीच एक सूक्ष्म लेकिन महत्वपूर्ण अंतर है । उत्तरार्द्ध वापसी नहीं करता है और अवरुद्ध करता रहता है, अगर कतार में पहले से ही देखा गया कोई संदेश है (उदाहरण के लिए, PeekMessage(PM_NOREMOVE) या GetQueueStatus ), लेकिन हटाया नहीं गया है। यह पम्पिंग के लिए अच्छा नहीं है, क्योंकि आपके कॉम ऑब्जेक्ट्स संदेश की कतार का निरीक्षण करने के लिए PeekMessage मेसेज जैसी कुछ चीजों का उपयोग कर रहे हैं। बाद में संभवतः MsgWaitForMultipleObjects को अवरुद्ध करने के लिए कारण हो सकता है जब उम्मीद नहीं की जाती है।

MsgWaitForMultipleObjectsEx , MsgWaitForMultipleObjectsEx साथ इस तरह की कमी नहीं है, और इस मामले में वापसी करेगा।

थोड़ी देर पहले मैंने एक अलग समस्या को हल करने के लिए StaTaskScheduler का एक कस्टम संस्करण बनाया ( StaTaskScheduler रूप में उपलब्ध है ): बाद में await लिए थ्रेड के साथ धागे के पूल को बनाए रखना। धागा संबंध बहुत जरूरी है यदि आप एसटीए कॉम ऑब्जेक्ट्स का उपयोग कई awaits । मूल StaTaskScheduler इस व्यवहार को केवल तब दर्शाता है जब इसका पूल 1 धागे तक सीमित है।

इसलिए मैंने आगे बढ़कर अपना WM_TEST केस के साथ कुछ और प्रयोग किया। मूलतः, मैंने STA थ्रेड पर मानक SynchronizationContext वर्ग का एक उदाहरण स्थापित किया था। WM_TEST संदेश पंप नहीं मिला, जो उम्मीद थी

फिर मैं SynchronizationContext.Wait SynchronizationContext.WaitHelperSynchronizationContext.WaitHelper । इसे केवल SynchronizationContext.WaitHelper । प्रतीक्षा करने के लिए अग्रेषित करें इसे बुलाया गया, लेकिन फिर भी पंप नहीं किया।

अंत में, मैंने एक पूर्ण विशेषताओं वाले संदेश पंप लूप को कार्यान्वित किया, यहां इसका मुख्य भाग है:

 // the core loop var msg = new NativeMethods.MSG(); while (true) { // MsgWaitForMultipleObjectsEx with MWMO_INPUTAVAILABLE returns, // even if there's a message already seen but not removed in the message queue nativeResult = NativeMethods.MsgWaitForMultipleObjectsEx( count, waitHandles, (uint)remainingTimeout, QS_MASK, NativeMethods.MWMO_INPUTAVAILABLE); if (IsNativeWaitSuccessful(count, nativeResult, out managedResult) || WaitHandle.WaitTimeout == managedResult) return managedResult; // there is a message, pump and dispatch it if (NativeMethods.PeekMessage(out msg, IntPtr.Zero, 0, 0, NativeMethods.PM_REMOVE)) { NativeMethods.TranslateMessage(ref msg); NativeMethods.DispatchMessage(ref msg); } if (hasTimedOut()) return WaitHandle.WaitTimeout; } 

यह काम करता है, WM_TEST को पंप किया जाता है नीचे आपके परीक्षण का एक अनुकूलित संस्करण है:

 public static async Task RunAsync() { using (var staThread = new Noseratio.ThreadAffinity.ThreadWithAffinityContext(staThread: true, pumpMessages: true)) { Console.WriteLine("Initial thread #" + Thread.CurrentThread.ManagedThreadId); await staThread.Run(async () => { Console.WriteLine("On STA thread #" + Thread.CurrentThread.ManagedThreadId); // create a simple Win32 window IntPtr hwnd = CreateTestWindow(); // Post some WM_TEST messages Console.WriteLine("Post some WM_TEST messages..."); NativeMethods.PostMessage(hwnd, NativeMethods.WM_TEST, new IntPtr(1), IntPtr.Zero); NativeMethods.PostMessage(hwnd, NativeMethods.WM_TEST, new IntPtr(2), IntPtr.Zero); NativeMethods.PostMessage(hwnd, NativeMethods.WM_TEST, new IntPtr(3), IntPtr.Zero); Console.WriteLine("Press Enter to continue..."); await ReadLineAsync(); Console.WriteLine("After await, thread #" + Thread.CurrentThread.ManagedThreadId); Console.WriteLine("Pending messages in the queue: " + (NativeMethods.GetQueueStatus(0x1FF) >> 16 != 0)); Console.WriteLine("Exiting STA thread #" + Thread.CurrentThread.ManagedThreadId); }, CancellationToken.None); } Console.WriteLine("Current thread #" + Thread.CurrentThread.ManagedThreadId); } 

आउटपुट :

 शुरुआती धागा # 9
 एसटीए थ्रेड # 10 पर
 कुछ WM_TEST संदेश पोस्ट करें ...
 जारी रखने के लिए Enter दबाएं ...
 WM_TEST संसाधित: 1
 WM_TEST संसाधित: 2
 WM_TEST संसाधित: 3

 इंतजार के बाद, थ्रेड # 10
 कतार में लंबित संदेश: गलत
 एसटीए धागा # 10 से बाहर निकलना
 वर्तमान धागा # 12
 बाहर निकलने के लिए कोई भी कुंजी दबाएँ 

ध्यान दें यह कार्यान्वयन दोनों थ्रेड एफ़िनिटी (यह await बाद थ्रेड # 10 पर रहता है) और संदेश पंपिंग दोनों को समर्थन देता है। पूर्ण स्रोत कोड में पुन: प्रयोज्य भागों ( ThreadAffinityTaskScheduler और ThreadWithAffinityContext ) शामिल हैं और यहां स्वयं उपलब्ध कंसोल ऐप के रूप में उपलब्ध है । इसे पूरी तरह से परीक्षण नहीं किया गया है, इसलिए इसे अपने जोखिम पर प्रयोग करें।

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

उस कोड के बारे में बात कर रहा है, जो एनएटी 2.0 में जोड़ा गया है, वह एक आंतरिक सीएलआर फंक्शन में मौजूद है जिसका नाम है मेस्वाइट हेल्पर ()। .NET 2.0 के लिए स्रोत कोड SSCLI20 वितरण के माध्यम से उपलब्ध है। बहुत पूर्ण है, लेकिन MsgWaitHelper () के लिए स्रोत शामिल नहीं है। सामान्य नहीं। इसे नष्ट करना बल्कि खो गया कारण है, यह बहुत बड़ी है

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

आप कुछ अंतर्दृष्टि प्राप्त कर सकते हैं कि आपके डिबगर के बिना प्रोग्राम को चलाकर प्रोग्राम को चलाकर और उसके बाद एक अप्रबंधित डीबगर संलग्न कर सकते हैं। आप इसे NtWaitForMultipleObjects () पर अवरुद्ध देखेंगे। मैं इसे एक कदम आगे ले गया और PeekMessageW () पर एक ब्रेकपैकेज सेट किया, इस स्टैक ट्रेस को प्राप्त करने के लिए:

 user32.dll!PeekMessageW() Unknown combase.dll!CCliModalLoop::MyPeekMessage(tagMSG * pMsg, HWND__ * hwnd, unsigned int min, unsigned int max, unsigned short wFlag) Line 2305 C++ combase.dll!CCliModalLoop::PeekRPCAndDDEMessage() Line 2008 C++ combase.dll!CCliModalLoop::FindMessage(unsigned long dwStatus) Line 2087 C++ combase.dll!CCliModalLoop::HandleWakeForMsg() Line 1707 C++ combase.dll!CCliModalLoop::BlockFn(void * * ahEvent, unsigned long cEvents, unsigned long * lpdwSignaled) Line 1645 C++ combase.dll!ClassicSTAThreadWaitForHandles(unsigned long dwFlags, unsigned long dwTimeout, unsigned long cHandles, void * * pHandles, unsigned long * pdwIndex) Line 46 C++ combase.dll!CoWaitForMultipleHandles(unsigned long dwFlags, unsigned long dwTimeout, unsigned long cHandles, void * * pHandles, unsigned long * lpdwindex) Line 120 C++ clr.dll!MsgWaitHelper(int,void * *,int,unsigned long,int) Unknown clr.dll!Thread::DoAppropriateWaitWorker(int,void * *,int,unsigned long,enum WaitMode) Unknown clr.dll!Thread::DoAppropriateWait(int,void * *,int,unsigned long,enum WaitMode,struct PendingSync *) Unknown clr.dll!CLREventBase::WaitEx(unsigned long,enum WaitMode,struct PendingSync *) Unknown clr.dll!CLREventBase::Wait(unsigned long,int,struct PendingSync *) Unknown clr.dll!Thread::Block(int,struct PendingSync *) Unknown clr.dll!SyncBlock::Wait(int,int) Unknown clr.dll!ObjectNative::WaitTimeout(bool,int,class Object *) Unknown 

सावधान रहें कि मैंने विंडोज 8.1 पर इस स्टैक ट्रेस को रिकॉर्ड किया है, यह पुराने विंडोज संस्करणों पर बहुत अलग दिखता है। विंडोज़ 8 में कॉम मोडल लूप को भारी मात्रा में तंग कर दिया गया है, यह भी WinRT कार्यक्रमों के लिए बहुत बड़ा सौदा है। इसके बारे में ज्यादा जानकारी नहीं है, लेकिन ऐसा लगता है कि एएसटीए नामित एक और एसटीए थ्रेडिंग मॉडल है जो एक अधिक प्रतिबंधात्मक तरह का पंप्सिंग करता है, जो कि जोड़ा गया CoWaitForMultipleObjects () में निहित है

ObjectNative :: WaitTimeout () है, जहां BlockingCollection.Take () विधि के अंदर semaphoreSlim.Wait () विधि CLR कोड निष्पादित करने के लिए शुरू होता है। आप इसे पौराणिक MsgWaitHelper () फ़ंक्शन पर पहुंचने के लिए आंतरिक सीएलआर कोड के स्तरों के माध्यम से खेती करते हैं, फिर कुख्यात COM मॉडल नोटपैरर लूप पर स्विच कर रहे हैं।

आपके कार्यक्रम में "गलत" तरह के पम्पिंग करने का बल्ले सिग्नल संकेत है CliModalLoop :: PeekRPCAndDDEMessage () विधि को कॉल करें। दूसरे शब्दों में, यह केवल उन इंटरॉप संदेशों पर विचार करता है जो एक विशिष्ट आंतरिक विंडो में पोस्ट किए जाते हैं जो कॉम कॉल को प्रेषित करता है जो एक अपार्टमेंट की सीमा को पार करती है। यह उन संदेशों को पंप नहीं करेगा जो संदेश विंडो में हैं जो आपकी खुद की विंडो के लिए हैं।

यह समझदार व्यवहार है, केवल विंडोज ही यह सुनिश्चित किया जा सकता है कि पुनःप्रसारक आपके प्रोग्राम को नहीं मार देगा, जब यह देख सकता है कि आपका UI थ्रेड बेकार है यह बेकार है जब यह संदेश लूप को पंप करता है, तो PeekMessage () या GetMessage () को कॉल यह स्थिति इंगित करता है। समस्या यह है, आप अपने आप को पंप नहीं करते हैं आपने एसटीए धागा के मुख्य अनुबंध का उल्लंघन किया है, इसे संदेश लूप पंप करना होगा । आशा है कि COM मोडल लूप आपके लिए पंपिंग करेगा, इस प्रकार बेकार आशा है।

आप वास्तव में इसे ठीक कर सकते हैं, भले ही मैं आप की सिफारिश नहीं करते। सीएलआर एक उचित रूप से निर्मित सिंक्रनाइज़ेशन कॉन्टैक्स। वर्तमान ऑब्जेक्ट द्वारा प्रतीक्षा करने के लिए इसे खुद आवेदन पर छोड़ देगा। आप अपना स्वयं का वर्ग बनाकर एक बना सकते हैं और प्रतीक्षा () विधि को ओवरराइड कर सकते हैं। CLR को समझने के लिए SetWaitNotificationRequired () विधि को कॉल करें कि इसे आपको छोड़ देना चाहिए एक अधूरा संस्करण जो दृष्टिकोण को दर्शाता है:

 class MySynchronizationProvider : System.Threading.SynchronizationContext { public MySynchronizationProvider() { base.SetWaitNotificationRequired(); } public override int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout) { for (; ; ) { int result = MsgWaitForMultipleObjects(waitHandles.Length, waitHandles, waitAll, millisecondsTimeout, 8); if (result == waitHandles.Length) System.Windows.Forms.Application.DoEvents(); else return result; } } [DllImport("user32.dll")] private static extern int MsgWaitForMultipleObjects(int cnt, IntPtr[] waitHandles, bool waitAll, int millisecondTimeout, int mask); } 

और इसे अपने थ्रेड की शुरुआत में इंस्टॉल करें:

  System.ComponentModel.AsyncOperationManager.SynchronizationContext = new MySynchronizationProvider(); 

अब आप अपने WM_TEST संदेश को प्रेषित हो देखेंगे। यह कॉल करने के लिए आवेदन। DoEVents () है कि यह भेजा। मैं इसे PeekMessage + DispatchMessage का उपयोग करके कवर कर सकता था लेकिन यह इस कोड के खतरे को दूर करेगा, सर्वोत्तम तालिका में DoEvents () छड़ी नहीं करने के लिए आप वास्तव में यहां एक बहुत ही खतरनाक फिर से प्रवेश कर रहे हैं। इस कोड का उपयोग न करें

लंबी कहानी छोटी है, StaThreadScheduler का उपयोग करने की एकमात्र आशा सही है, जब वह कोड में प्रयोग किया जाता है जो एसटीए अनुबंध को लागू करता है और एक एसटीए धागा की तरह पंपों को करना चाहिए। यह वास्तव में पुरानी कोड के लिए बैंड-सहायता के रूप में था, जहां आपको थ्रेड स्थिति को नियंत्रित करने के लिए लक्जरी नहीं है। किसी भी ऐसे कोड की तरह जो VB6 प्रोग्राम या Office ऐड-इन में जीवन शुरू कर दिया। इसके साथ थोड़ा सा प्रयोग, मुझे नहीं लगता कि यह वास्तव में काम कर सकता है। उल्लेखनीय यह भी है कि इसके लिए जरूरी आइश की उपलब्धता के साथ पूरी तरह समाप्त हो जाना चाहिए।