2011-12-31 10 views
15

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

http://msdn.microsoft.com/en-us/magazine/cc164086.aspx

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

इस प्रश्न को पोस्ट करने का कारण एक फाइबर के अंदर अपवाद फेंकने के साथ करना है। लेख के मुताबिक, CreateLogicalThreadState(), SwitchOutLogicalThreadState(), आदि के साथ CorBindToRunTime API का उपयोग करके, फ्रेमवर्क प्रत्येक फाइबर के लिए एक प्रबंधित थ्रेड बनाएगा और अपवादों को सही तरीके से संभाल देगा।

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

यह मेरा तात्पर्य है कि SwitchOutLogicalThreadState() और SwitchInLogicalThreadState() शायद ठीक से उपयोग नहीं किया जा रहा है या अन्यथा वे अपना काम नहीं कर रहे हैं।

नोट: समस्या का एक संकेत यह है कि प्रबंधित कोड Thread.CurrentThread.ManagedThreadId लॉग करता है और यह प्रत्येक फाइबर के लिए समान है। इससे पता चलता है कि CreateLogicalThreadState() विधि वास्तव में विज्ञापित के रूप में एक नया प्रबंधित धागा नहीं बना था।

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

यह पता के रूप में सिर्फ एक धागा ऐसा है तो यह एक फाइबर में धर्मान्तरित शुरू होता है:

  1. ConvertThreadToFiber (objptr);
  2. CreateFiber() // कई win32 फाइबर बनाएं।

अब एक फाइबर पहली बार आह्वान, यह स्टार्टअप विधि करता है:

  1. corhost-> SwitchOutLogicalThreadState (& कुकी); स्टैक पर आयोजित मुख्य कुकी है।
  2. SwitchToFiber(); // पहली बार फाइबर स्टार्टअप विधि
  3. corhost-> CreateLogicalThreadState();
  4. मुख्य फाइबर सार विधि चलाएं।

अंततः फाइबर मुख्य फाइबर के लिए वापस उपज के लिए की जरूरत है:

  1. corhost-> SwitchOutLogicalThreadState (& कुकी);
  2. स्विचटोफाइबर (फाइबर);
  3. corhost-> SwitchInLogicalThreadState (कुकी); // मुख्य फाइबर कुकी, है ना?

इसके अलावा मुख्य फाइबर एक पहले से मौजूद फाइबर फिर से शुरू होगा:

  1. corhost-> SwitchOutLogicalThreadState (& कुकी);
  2. स्विचटोफाइबर (फाइबर);
  3. corhost-> SwitchInLogicalThreadState (& कुकी); // मुख्य फाइबर कुकी, है ना?

निम्नलिखित fibers.cpp है जो प्रबंधित कोड के लिए फाइबर एपीआई को लपेटता है।

#define _WIN32_WINNT 0x400 

#using <mscorlib.dll> 
#include <windows.h> 
#include <mscoree.h> 
#include <iostream> 
using namespace std; 

#if defined(Yield) 
#undef Yield 
#endif 

#define CORHOST 

namespace Fibers { 

typedef System::Runtime::InteropServices::GCHandle GCHandle; 

VOID CALLBACK unmanaged_fiberproc(PVOID pvoid); 

__gc private struct StopFiber {}; 

enum FiberStateEnum { 
    FiberCreated, FiberRunning, FiberStopPending, FiberStopped 
}; 

#pragma unmanaged 

#if defined(CORHOST) 
ICorRuntimeHost *corhost; 

void initialize_corhost() { 
    CorBindToCurrentRuntime(0, CLSID_CorRuntimeHost, 
     IID_ICorRuntimeHost, (void**) &corhost); 
} 

#endif 

void CorSwitchToFiber(void *fiber) { 
#if defined(CORHOST) 
    DWORD *cookie; 
    corhost->SwitchOutLogicalThreadState(&cookie); 
#endif 
    SwitchToFiber(fiber); 
#if defined(CORHOST) 
    corhost->SwitchInLogicalThreadState(cookie); 
#endif 
} 

#pragma managed 

__gc __abstract public class Fiber : public System::IDisposable { 
public: 
#if defined(CORHOST) 
    static Fiber() { initialize_corhost(); } 
#endif 
    Fiber() : state(FiberCreated) { 
     void *objptr = (void*) GCHandle::op_Explicit(GCHandle::Alloc(this)); 
     fiber = ConvertThreadToFiber(objptr); 
     mainfiber = fiber; 
     //System::Console::WriteLine(S"Created main fiber."); 
} 

    Fiber(Fiber *_mainfiber) : state(FiberCreated) { 
     void *objptr = (void*) GCHandle::op_Explicit(GCHandle::Alloc(this)); 
     fiber = CreateFiber(0, unmanaged_fiberproc, objptr); 
     mainfiber = _mainfiber->fiber; 
     //System::Console::WriteLine(S"Created worker fiber"); 
    } 

    __property bool get_IsRunning() { 
     return state != FiberStopped; 
    } 

    int GetHashCode() { 
     return (int) fiber; 
    } 


    bool Resume() { 
     if(!fiber || state == FiberStopped) { 
      return false; 
     } 
     if(state == FiberStopPending) { 
      Dispose(); 
      return false; 
     } 
     void *current = GetCurrentFiber(); 
     if(fiber == current) { 
      return false; 
     } 
     CorSwitchToFiber(fiber); 
     return true; 
    } 

    void Dispose() { 
     if(fiber) { 
      void *current = GetCurrentFiber(); 
      if(fiber == current) { 
       state = FiberStopPending; 
       CorSwitchToFiber(mainfiber); 
      } 
      state = FiberStopped; 
      System::Console::WriteLine(S"\nDeleting Fiber."); 
      DeleteFiber(fiber); 
      fiber = 0; 
     } 
    } 
protected: 
    virtual void Run() = 0; 


    void Yield() { 
     CorSwitchToFiber(mainfiber); 
     if(state == FiberStopPending) 
      throw new StopFiber; 
    } 
private: 
    void *fiber, *mainfiber; 
    FiberStateEnum state; 

private public: 
    void main() { 
     state = FiberRunning; 
     try { 
      Run(); 
     } catch(System::Object *x) { 
      System::Console::Error->WriteLine(
       S"\nFIBERS.DLL: main Caught {0}", x); 
     } 
     Dispose(); 
    } 
}; 

void fibermain(void* objptr) { 
    //System::Console::WriteLine( S"\nfibermain()"); 
    System::IntPtr ptr = (System::IntPtr) objptr; 
    GCHandle g = GCHandle::op_Explicit(ptr); 
    Fiber *fiber = static_cast<Fiber*>(g.Target); 
    g.Free(); 
    fiber->main(); 
    System::Console::WriteLine(S"\nfibermain returning"); 
} 

#pragma unmanaged 

VOID CALLBACK unmanaged_fiberproc(PVOID objptr) { 
#if defined(CORHOST) 
    corhost->CreateLogicalThreadState(); 
#endif 
    fibermain(objptr); 
#if defined(CORHOST) 
    corhost->DeleteLogicalThreadState(); 
#endif 
} 

} 

उपरोक्त fibers.cpp क्लास फ़ाइल Visaul C++ प्रोजेक्ट में एकमात्र कक्षा है। इसे सीएलआर समर्थन के साथ डीएलएल के रूप में बनाया गया है/सीएलआर: पुराना स्टाइल स्विच।

using System; 
using System.Threading; 
using Fibers; 
using NUnit.Framework; 

namespace TickZoom.Utilities 
{ 
    public class FiberTask : Fiber 
    { 
     public FiberTask() 
     { 

     } 
     public FiberTask(FiberTask mainTask) 
      : base(mainTask) 
     { 

     } 

     protected override void Run() 
     { 
      while (true) 
      { 
       Console.WriteLine("Top of worker loop."); 
       try 
       { 
        Work(); 
       } 
       catch (Exception ex) 
       { 
        Console.WriteLine("Exception: " + ex.Message); 
       } 
       Console.WriteLine("After the exception."); 
       Work(); 
      } 
     } 

     private void Work() 
     { 
      Console.WriteLine("Doing work on fiber: " + GetHashCode() + ", thread id: " + Thread.CurrentThread.ManagedThreadId); 
      ++counter; 
      Console.WriteLine("Incremented counter " + counter); 
      if (counter == 2) 
      { 
       Console.WriteLine("Throwing an exception."); 
       throw new InvalidCastException("Just a test exception."); 
      } 
      Yield(); 
     } 

     public static int counter; 
    } 

    [TestFixture] 
    public class TestingFibers 
    { 
     [Test] 
     public void TestIdeas() 
     { 
      var fiberTasks = new System.Collections.Generic.List<FiberTask>(); 
      var mainFiber = new FiberTask(); 
      for(var i=0; i< 5; i++) 
      { 
       fiberTasks.Add(new FiberTask(mainFiber)); 
      } 
      for (var i = 0; i < fiberTasks.Count; i++) 
      { 
       Console.WriteLine("Resuming " + i); 
       var fiberTask = fiberTasks[i]; 
       if(!fiberTask.Resume()) 
       { 
        Console.WriteLine("Fiber " + i + " was disposed."); 
        fiberTasks.RemoveAt(i); 
        i--; 
       } 
      } 
      for (var i = 0; i < fiberTasks.Count; i++) 
      { 
       Console.WriteLine("Disposing " + i); 
       fiberTasks[i].Dispose(); 
      } 
     } 
    } 
} 

ऊपर इकाई परीक्षण निम्न उत्पादन देता है और फिर बुरी तरह से दुर्घटनाओं:

Resuming 0 
Top of worker loop. 
Doing work on fiber: 476184704, thread id: 7 
Incremented counter 1 
Resuming 1 
Top of worker loop. 
Doing work on fiber: 453842656, thread id: 7 
Incremented counter 2 
Throwing an exception. 
Exception: Just a test exception. 
After the exception. 
+1

और आप क्या सी #/एफएक्स संस्करण का उपयोग कर रहे हैं? मूल को Fx2 –

+0

के लिए पहले से ही फ्लैकी माना जाता था यह सी # 3.5 है। तो क्या यह निराशाजनक है? हम उच्च प्रदर्शन शेड्यूलिंग के लिए थ्रेड स्टेट को स्टोर और फिर से शुरू करने के तरीके को समझने के लिए बेताब हैं। मुझे यह अन्य प्रश्न मिला है जो अधिक सामान्य है और हमें इस क्षमता की आवश्यकता क्यों है, इसकी पृष्ठभूमि बताती है। समाधान के लिए कोई अन्य विचार सबसे स्वागत है! http://stackoverflow.com/questions/8685806/c-sharp-first-class-continuation-via-c-interop-or-some-other-way – Wayne

+3

विकल्पों के साथ एक संबंधित SO धागा: [क्या कोई फाइबर एपीआई है .NET?] (http://stackoverflow.com/questions/1949051/is-there-a-fiber-api-in-net) –

उत्तर

2

एक समय पहले, मैं एक ही समस्या का सामना करना - मैं .NET 3.5 में कोड का उपयोग करने की कोशिश की (जो बाद में 4.0 पर) और यह दुर्घटनाग्रस्त हो गया। इसने मुझे "हैकी" समाधान से दूर जाने के लिए आश्वस्त किया। सच्चाई यह है कि .NET में एक सामान्य सह-रूटीन अवधारणा गुम है। ऐसे कुछ लोग हैं जो गणक द्वारा सह-रूटीन अनुकरण करते हैं और yield कीवर्ड (http://fxcritic.blogspot.com/2008/05/lightweight-fibercoroutines.html देखें)। हालांकि, यह मेरे लिए स्पष्ट नुकसान है: यह पुराने पुराने Win32 फाइबर के रूप में उपयोग करने के लिए सहज नहीं है और इसके लिए आपको प्रत्येक सह-दिनचर्या के लिए रिटर्न प्रकार के रूप में IEnumerable का उपयोग करने की आवश्यकता है।

हो सकता है कि इस arcticle: http://msdn.microsoft.com/en-us/vstudio/gg316360 आप के लिए दिलचस्प है। माइक्रोसॉफ्ट एक नया async कीवर्ड पेश करने जा रहा है। डाउनलोड के लिए एक सामुदायिक प्रौद्योगिकी पूर्वावलोकन (सीटीपी) की पेशकश की जाती है। मुझे लगता है कि उन async एक्सटेंशन के शीर्ष पर एक स्वच्छ सह-नियमित कार्यान्वयन विकसित करना संभव होना चाहिए।

+0

अच्छा, धन्यवाद लेकिन एक और कारक खेलने आया है। हमें यह सब काम ऐपडोमेन्स में रखने की क्षमता की भी आवश्यकता है ताकि प्लगइन्स को अनलोड/गतिशील रूप से लोड किया जा सके। तो ऐसा लगता है कि सीएलआर का अपना कस्टम सी ++ होस्ट बनाने के लिए नेट 2.0 एसडीके के लिए एक कूप फाइबर नमूना कोड है और फाइबर के साथ अपना खुद का थ्रेड पूल प्रदाता है। हम निकट भविष्य में ऐसा करने और परीक्षण करने की योजना बना रहे हैं। – Wayne

+0

मेरी आखिरी टिप्पणी स्क्रैच करें। ओह! 2.0 अब से निपटने के लिए बहुत पुराना है। मैं क्या सोच रहा था? मेजबान में भी कूप फाइबर अब समर्थित नहीं है। शायद async एकमात्र तरीका है ... लेकिन क्या यह संदर्भ स्विचिंग का कारण बनता है? इसका परीक्षण करना होगा। – Wayne

0

फाइबर का उपयोग जब आप एक स्थानीय चर पर अपवाद प्रबंधन ढेर राज्य (स्टैक पर) की दुकान चाहिए इससे पहले कि आप अपने मुख्य फाइबर पर स्विच करें। स्विच के बाद सही ऑपरेशन (जब निष्पादन वापस आता है) स्थानीय चर में आपके बैकअप से अपवाद स्टैक को पुनर्स्थापित कर रहा है। अपवाद संचालन को तोड़ने के बिना कैसे डेल्फी के साथ फाइबर का उपयोग करने पर इस ब्लॉग प्रविष्टि पर एक नज़र डालें: http://jsbattig.blogspot.com/2015/03/how-to-properly-support-windows-fibers.html

बिंदु यह है कि आप रेशे का उपयोग और अपवाद संचालकों लिख सकते हैं और अंदर फाइबर स्विच और अंत में प्रयास करें या कैच ब्लॉक की कोशिश करना चाहते हैं , आपको यह पता लगाना होगा कि सीएलआर के साथ ऐसा कैसे करें।

मैं सी # में फाइबर के साथ चारों ओर खेल रहा हूँ और मैं अभी तक जिस तरह से नहीं मिल सका। अगर ऐसा करने का कोई तरीका था, तो मुझे लगता है कि यह दिन के अंत में एक हैक होगा।

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