Okay, poniższy link zawiera ostrzeżenie, że dyskusja używa nieudokumentowanych i nieudokumentowanych apisów. Próbuję użyć próbki kodu w dowolny sposób. To głównie działa. Wszelkie pomysły dotyczące określonego problemu poniżej dotyczące wyjątków?Używanie zarządzanych wątków i włókien w CLR
http://msdn.microsoft.com/en-us/magazine/cc164086.aspx
FYI, zrobiłem poprawę w stosunku do oryginalnej próbki. Utrzymywał wskaźnik do "poprzedniego włókna". Zamiast tego zaktualizowana próbka poniżej wykorzystuje wskaźnik "mainfiber", który zostaje przekazany do każdej klasy światłowodu. W ten sposób zawsze wracają do głównego włókna. To pozwala głównemu światłu na planowanie harmonogramowania dla wszystkich innych włókien. Pozostałe włókna zawsze "oddają" z powrotem do głównego włókna.
Powód umieszczenia tego pytania wiąże się z rzucaniem wyjątków we włókno. Zgodnie z tym artykułem, używając CorBindToRunTime API z CreateLogicalThreadState(), SwitchOutLogicalThreadState(), itp., Framework stworzy zarządzany wątek dla każdego włókna i poprawnie obsłuży wyjątki.
Jednak w załączonych przykładach kodu posiada on test UUnit, który eksperymentuje z wyrzucaniem zarządzanego wyjątku w obrębie Fibre i przechwytywaniem go w tym samym włóknie. Ta miękka robota. Ale po obsłużeniu go przez zalogowanie komunikatu wydaje się, że stos jest w złym stanie, ponieważ jeśli światłowód wywoła dowolną inną metodę, nawet pustą, cała aplikacja ulega awarii.
To sugeruje, że SwitchOutLogicalThreadState() i SwitchInLogicalThreadState() być może nie są używane poprawnie, a może nie wykonują swojej pracy.
UWAGA: Jedna wskazówka dotycząca problemu polega na tym, że zarządzany kod wylogowuje wątek.CurrentThread.ManagedThreadId i jest taki sam dla każdego włókna. Sugeruje to, że metoda CreateLogicalThreadState() tak naprawdę nie utworzyła nowego zarządzanego wątku zgodnie z reklamą.
Aby to lepiej przeanalizować, utworzyłem pseudokodową listę kolejności interfejsów API niskiego poziomu, które są używane do obsługi włókien. Pamiętaj, że wszystkie włókna biegną na tej samej nici, więc nic się nie dzieje, jest to logika liniowa. Oczywiście koniecznym trikiem jest zapisanie i przywrócenie stosu. Tam wydaje się, że ma kłopoty.
Zaczyna się po prostu jako gwint tak następnie konwertuje się do włókna:
- ConvertThreadToFiber (objptr);
- CreateFiber() // utwórz kilka włókien win32.
Teraz powołać włókno po raz pierwszy, jest to metoda uruchamiania robi to:
- corhost-> SwitchOutLogicalThreadState (& cookie); Główny plik cookie to przechowywany na stosie.
- SwitchToFiber(); // po raz pierwszy wywołuje metodę uruchamiania włókien:
- corhost-> CreateLogicalThreadState();
- uruchomić główną metodę abstrakcyjnego włókna.
końcu włókna musi wytworzeniem do głównego włókna:
- corhost-> SwitchOutLogicalThreadState (& cookie);
- SwitchToFiber (światłowód);
- corhost-> SwitchInLogicalThreadState (& cookie); // główny plik cookie z włókna, , prawda?
także głównym włókno będzie wznowić zarejestrowanej wcześniej surowcowy:
- corhost-> SwitchOutLogicalThreadState (& cookie);
- SwitchToFiber (światłowód);
- corhost-> SwitchInLogicalThreadState (& cookie); // główny plik cookie z włókna, prawda?
Poniżej przedstawiono plik fibers.cpp, który otacza włókno api dla kodu zarządzanego.
#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
}
}
Powyższy plik klasy fibers.cpp jest jedyną klasą w projekcie Visa C++. Jest zbudowany jako DLL z obsługą CLR przy użyciu/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();
}
}
}
}
Powyższe badanie jednostka daje następujące dane wyjściowe, a następnie zawiesza się źle:
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.
Jakiej wersji C#/Fx używasz? Oryginał został już uznany za niestabilny dla Fx2 –
To jest C# 3.5. Czy to jest beznadziejne? Chcielibyśmy dowiedzieć się, jak zapisać i wznowić stan wątku, aby zapewnić wysoką wydajność planowania. Mam inne pytanie, które jest bardziej ogólne i wyjaśnia, dlaczego potrzebujemy tej możliwości. Wszelkie inne pomysły na rozwiązania są mile widziane! http://stackoverflow.com/questions/8685806/c-sharp-first-class-continuation-via-c-interop-us-some-other-way – Wayne
Związany wątek SO z alternatywami: [Czy w aplikacji dostępne jest włókno api .net?] (http://stackoverflow.com/questions/1949051/is-there-a-fiber-api-in-net) –