16

さて、次のリンクには、ディスカッションでサポートされておらず、文書化されていない API が使用されているという警告があります。とにかく、コードサンプルを使用しようとしています。それは主に動作します。例外に関連する以下の特定の問題についてのアイデアはありますか?

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

参考までに、元のサンプルを改良しました。「前のファイバー」へのポインターを維持していました。代わりに、以下の更新されたサンプルでは、​​すべてのファイバー クラスに渡される「メインファイバー」ポインターを使用しています。そのようにして、それらは常に主繊維に戻ります。これにより、メイン ファイバーが他のすべてのファイバーのスケジューリングを処理できるようになります。他のファイバーは、常にメインのファイバーに「屈服」します。

この質問を投稿する理由は、ファイバー内で例外をスローすることに関係しています。記事によると、CorBindToRunTime API を CreateLogicalThreadState()、SwitchOutLogicalThreadState() などで使用することにより、フレームワークはファイバーごとにマネージド スレッドを作成し、適切に例外を処理します。

ただし、含まれているコード例には、ファイバー内でマネージ例外をスローし、同じファイバー内でそれをキャッチすることを実験する UUnit テストがあります。その作品のソフト。しかし、メッセージをログに記録して処理した後、ファイバーが空のメソッドであっても他のメソッドを呼び出すと、アプリケーション全体がクラッシュするため、スタックが悪い状態にあるようです。

これは、SwitchOutLogicalThreadState() と SwitchInLogicalThreadState() が適切に使用されていないか、機能していない可能性があることを意味します。

注: この問題の手がかりの 1 つは、マネージ コードが Thread.CurrentThread.ManagedThreadId をログアウトすることであり、これはすべてのファイバーで同じです。これは、CreateLogicalThreadState() メソッドが宣伝どおりに新しいマネージド スレッドを実際に作成しなかったことを示唆しています。

これをよりよく分析するために、ファイバーを処理するために呼び出される低レベル API の順序の疑似コード リストを作成しました。ファイバーはすべて同じスレッドで実行されるため、同時に何も起こらないことを覚えておいてください。これは線形ロジックです。もちろん必要なトリックは、スタックを保存して復元することです。そこが困っているようです。

それは単純な糸として始まり、その後繊維に変わります。

  1. ConvertThreadToFiber(objptr);
  2. CreateFiber() // 複数の win32 ファイバーを作成します。

ここで、初めてファイバーを呼び出します。そのスタートアップ メソッドは次のことを行います。

  1. corhost->SwitchOutLogicalThreadState(&cookie); メイン Cookie はスタックに保持されます。
  2. SwitchToFiber(); // 初めてファイバー起動メソッドを呼び出します
  3. corhost->CreateLogicalThreadState();
  4. メインのファイバー抽象メソッドを実行します。

最終的に、ファイバーはメイン ファイバーに戻る必要があります。

  1. corhost->SwitchOutLogicalThreadState(&cookie);
  2. SwitchToFiber(ファイバー);
  3. corhost->SwitchInLogicalThreadState(&cookie); // メインのファイバー クッキーですね。

また、メイン ファイバーは既存のファイバーを再開します。

  1. corhost->SwitchOutLogicalThreadState(&cookie);
  2. SwitchToFiber(ファイバー);
  3. corhost->SwitchInLogicalThreadState(&cookie); // メインのファイバー クッキーですね。

以下は、マネージド コード用のファイバー API をラップする fiber.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
}

}

上記の fiber.cpp クラス ファイルは、Visaul c++ プロジェクトの唯一のクラスです。/CLR:oldstyle スイッチを使用して、CLR をサポートする DLL としてビルドされます。

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.
4

3 に答える 3

2

少し前に、同じ問題が発生しました。.NET3.5(4.0以降)でコードスニペットを使用しようとすると、クラッシュしました。これにより、私は「ハッキー」なソリューションから目をそらすようになりました。真実は、.NETには一般的なコルーチンの概念が欠けているということです。yield列挙子とキーワードによってコルーチンをシミュレートする人がいます( http://fxcritic.blogspot.com/2008/05/lightweight-fibercoroutines.htmlを参照)。ただし、これには明らかな欠点があります。古き良きWin32ファイバーほど直感的に使用できずIEnumerable、各コルーチンのリターンタイプとして使用する必要があります。

多分このアークティクル:http://msdn.microsoft.com/en-us/vstudio/gg316360あなたにとって興味深いものです。asyncMicrosoftは新しいキーワードを導入しようとしています。コミュニティテクノロジープレビュー(CTP)がダウンロード用に提供されています。これらの非同期拡張機能に加えて、クリーンなコルーチン実装を開発できるはずです。

于 2012-01-05T16:03:38.460 に答える