2011-12-31 27 views
15

Ok, liên kết sau có cảnh báo rằng cuộc thảo luận sử dụng apis không được hỗ trợ và không được cấp phép. Tôi đang cố gắng sử dụng mẫu mã bất kỳ cách nào. Nó chủ yếu hoạt động. Bất kỳ ý tưởng nào về vấn đề cụ thể bên dưới có liên quan đến ngoại lệ?Sử dụng chuỗi và sợi được quản lý trong CLR

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

FYI, tôi đã cải tiến so với mẫu ban đầu. Nó đã được duy trì một con trỏ đến "khung trước". Thay vào đó, mẫu được cập nhật bên dưới sử dụng một con trỏ "mainfiber" được chuyển tới mọi lớp sợi. Bằng cách đó, chúng luôn mang lại chất xơ chính. Điều đó cho phép các sợi chính để xử lý lập kế hoạch cho tất cả các sợi khác. Các sợi khác luôn "thu" trở lại sợi chính.

Lý do đăng câu hỏi này có liên quan đến việc ném ngoại lệ bên trong sợi quang. Theo bài viết, bằng cách sử dụng API CorBindToRunTime với CreateLogicalThreadState(), SwitchOutLogicalThreadState(), vv, khung công tác sẽ tạo một luồng được quản lý cho mỗi sợi và xử lý đúng các ngoại lệ.

Tuy nhiên, trong các ví dụ mã được bao gồm, nó có một bài kiểm tra UUnit thử nghiệm với ném một ngoại lệ quản lý trong một sợi và cũng bắt nó trong cùng một chất xơ. Đó là công việc mềm. Nhưng sau khi xử lý nó bằng cách đăng nhập một tin nhắn, có vẻ như ngăn xếp ở trạng thái xấu vì nếu sợi gọi bất kỳ phương thức nào khác ngay cả một phương thức trống, toàn bộ ứng dụng bị treo.

Điều này hàm ý với tôi rằng SwitchOutLogicalThreadState() và SwitchInLogicalThreadState() có thể không được sử dụng đúng cách hoặc có thể họ không thực hiện công việc của mình.

LƯU Ý: Một đầu mối cho vấn đề là mã được quản lý sẽ ghi lại Thread.CurrentThread.ManagedThreadId và nó giống nhau đối với mỗi sợi. Điều này cho thấy phương thức CreateLogicalThreadState() không thực sự tạo một luồng được quản lý mới như được quảng cáo.

Để phân tích điều này tốt hơn, tôi đã tạo danh sách mã giả của thứ tự các API cấp thấp được gọi để xử lý các sợi. Hãy nhớ rằng, tất cả các sợi đều chạy trên cùng một chuỗi sao cho không có gì xảy ra đồng thời, nó là một logic tuyến tính. Bí quyết cần thiết của khóa học là để lưu và khôi phục ngăn xếp. Đó là nơi mà nó có vẻ gặp rắc rối.

Nó bắt đầu ra như đơn giản là một chủ đề như vậy thì nó chuyển thành một sợi:

  1. ConvertThreadToFiber (objptr);
  2. CreateFiber() // tạo một số sợi win32.

Bây giờ gọi một sợi lần đầu tiên, đó là phương pháp khởi động thực hiện điều này:

  1. corhost-> SwitchOutLogicalThreadState (& cookie); Cookie chính là được giữ trên ngăn xếp.
  2. SwitchToFiber(); // lần đầu tiên gọi phương thức khởi động sợi
  3. corhost-> CreateLogicalThreadState();
  4. chạy phương pháp trừu tượng sợi chính.

Cuối cùng, chất xơ cần phải mang lại cho sợi chính:

  1. corhost-> SwitchOutLogicalThreadState (& cookie);
  2. SwitchToFiber (sợi);
  3. corhost-> SwitchInLogicalThreadState (& cookie); // sợi chính cookie, phải không?

Cũng sợi chính sẽ tiếp tục một sợi từ trước:

  1. corhost-> SwitchOutLogicalThreadState (& cookie);
  2. SwitchToFiber (sợi);
  3. corhost-> SwitchInLogicalThreadState (& cookie); // cookie sợi chính, phải không?

Sau đây là sợi.cpp bao bọc sợi api cho mã được quản lý.

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

} 

Các tập tin trên lớp.cpp là lớp duy nhất trong dự án Visaul C++. Nó được xây dựng như một DLL với CLR hỗ trợ bằng cách sử dụng/CLR: oldstyle switch.

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

Các đơn vị kiểm tra trên cho đầu ra sau và sau đó bị treo nặng:

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

Và bạn đang sử dụng phiên bản C#/Fx nào? Bản gốc đã được coi là flaky cho Fx2 –

+0

Đây là C# 3.5. Vì vậy, nó là vô vọng? Chúng tôi đang tuyệt vọng để tìm ra cách lưu trữ và tiếp tục trạng thái luồng để lập lịch biểu hiệu suất cao. Tôi đã có câu hỏi khác đó là tổng quát hơn và giải thích lý do tại sao chúng ta cần khả năng này. Bất kỳ ý tưởng khác cho các giải pháp được chào đón nhiều nhất! http://stackoverflow.com/questions/8685806/c-sharp-first-class-continuation-via-c-interop-or-some-other-way – Wayne

+3

Một chuỗi SO có liên quan với các lựa chọn thay thế: [Có sợi api nào trong .net?] (http://stackoverflow.com/questions/1949051/is-there-a-fiber-api-in-net) –

Trả lời

2

Một thời gian trước đây, tôi có kinh nghiệm cùng một vấn đề - Tôi cố gắng để sử dụng đoạn mã trong .NET 3.5 (sau này trên 4.0) và nó bị hỏng. Điều này đã thuyết phục tôi từ bỏ giải pháp "hacky". Sự thật là .NET thiếu một khái niệm chung thường xuyên. Có một số người mô phỏng các đồng nghiệp của các điều tra viên và từ khóa yield (xem http://fxcritic.blogspot.com/2008/05/lightweight-fibercoroutines.html). Tuy nhiên, điều này có nhược điểm rõ ràng với tôi: Nó không phải là trực quan để sử dụng như sợi Win32 tốt cũ và nó đòi hỏi bạn phải sử dụng IEnumerable như là loại trả lại cho mỗi đồng thường xuyên.

Có thể arcticle này: http://msdn.microsoft.com/en-us/vstudio/gg316360 là điều thú vị đối với bạn. Microsoft sắp giới thiệu một từ khóa async mới. Bản xem trước công nghệ cộng đồng (CTP) được cung cấp để tải xuống. Tôi đoán nó sẽ có thể để phát triển một thực hiện đồng thường xuyên sạch sẽ trên đầu trang của những phần mở rộng async.

+0

Vâng, cảm ơn nhưng một yếu tố khác đã đến để chơi. Chúng tôi cũng cần khả năng có tất cả công việc này trên AppDomains để các plugin có thể được tải/tải động. Vì vậy, có vẻ như có một mã mẫu Coop Fiber cho .Net 2.0 SDK để xây dựng máy chủ C++ tùy chỉnh của riêng bạn của CLR và cung cấp nhóm chủ đề của riêng bạn với các sợi. Chúng tôi dự định nghiên cứu và thử nghiệm làm điều đó trong tương lai gần. – Wayne

+0

Gãi nhận xét cuối cùng của tôi. Duh! 2.0 quá cũ để xử lý ngay bây giờ. Tôi đang nghĩ gì vậy? Coop sợi ngay cả trong máy chủ không được hỗ trợ nữa. Có lẽ async là cách duy nhất ... nhưng nó có gây ra chuyển đổi ngữ cảnh không? Sẽ phải kiểm tra nó. – Wayne

0

Khi sử dụng sợi, bạn phải lưu trữ trạng thái ngăn xếp quản lý ngoại lệ trên biến cục bộ (trên ngăn xếp) trước khi chuyển sang sợi chính. Hoạt động đầu tiên ngay sau khi chuyển đổi (khi thực hiện trở lại) đang khôi phục ngăn xếp ngoại lệ từ bản sao lưu của bạn trong một biến cục bộ. Hãy xem bài viết blog này về cách sử dụng sợi với Delphi mà không vi phạm xử lý ngoại lệ: http://jsbattig.blogspot.com/2015/03/how-to-properly-support-windows-fibers.html

Vấn đề là, nếu bạn muốn sử dụng sợi và ghi xử lý ngoại lệ và chuyển sợi bên trong và cố gắng cuối cùng hoặc thử bắt khối , bạn sẽ phải tìm ra cách để làm điều này với CLR.

Tôi đang phát xung quanh bằng Fibers in C# và tôi chưa thể tìm được đường. Nếu có một cách để làm điều đó, tôi tưởng tượng nó sẽ là một hack vào cuối ngày.

Các vấn đề liên quan