Ok, il seguente collegamento ha un avvertimento che la discussione utilizza apis non supportati e non documentati. Beh, sto provando a usare il codice di esempio in qualsiasi modo. Funziona principalmente. Qualche idea sul problema specifico di seguito relativo alle eccezioni?Utilizzo di thread e fibre gestiti in CLR
http://msdn.microsoft.com/en-us/magazine/cc164086.aspx
Cordiali saluti, ho fatto un miglioramento rispetto al campione originale. Stava mantenendo un puntatore al "precedentefibre". Invece, l'esempio aggiornato sotto utilizza un puntatore "mainfiber" che viene passato a ogni classe di fibra. In questo modo, ritornano sempre alla fibra principale. Ciò consente alla fibra principale di gestire la pianificazione per tutte le altre fibre. Le altre fibre "restituiscono" sempre alla fibra principale.
Il motivo per la pubblicazione di questa domanda ha a che fare con il lancio di eccezioni all'interno di una fibra. Secondo l'articolo, utilizzando l'API CorBindToRunTime con CreateLogicalThreadState(), SwitchOutLogicalThreadState(), ecc., Il framework creerà un thread gestito per ciascuna fibra e gestirà correttamente le eccezioni.
Tuttavia, negli esempi di codice incluso ha un test UUnit che sperimenta il lancio di un'eccezione gestita all'interno di una fibra e la cattura anche all'interno della stessa fibra. Questo morbido di opere. Ma dopo averlo gestito registrando un messaggio, sembra che lo stack sia in uno stato negativo perché se la fibra chiama qualsiasi altro metodo, anche un metodo vuoto, l'intera applicazione si blocca.
Ciò significa che SwitchOutLogicalThreadState() e SwitchInLogicalThreadState() potrebbero non essere utilizzati correttamente oppure potrebbero non funzionare.
NOTA: un indizio del problema è che il codice gestito disconnette Thread.CurrentThread.ManagedThreadId ed è lo stesso per ogni fibra. Ciò suggerisce che il metodo CreateLogicalThreadState() non ha realmente creato un nuovo thread gestito come pubblicizzato.
Per analizzare questo meglio, ho fatto un elenco pseudocodice dell'ordine delle API di basso livello chiamate per gestire le fibre. Ricorda, che tutte le fibre girano sullo stesso thread in modo che non accada nulla di simile, è una logica lineare. Il trucco necessario, naturalmente, è salvare e ripristinare lo stack. È lì che sembra che ci siano problemi.
inizia come un semplice filo è così allora converte in una fibra:
- ConvertThreadToFiber (ObjPtr);
- CreateFiber() // crea diverse fibre win32.
Ora richiamare una fibra per la prima volta, è il metodo di avvio fa questo:
- corhost-> SwitchOutLogicalThreadState (& cookie); Il cookie principale è tenuto in pila.
- SwitchToFiber(); // la prima volta chiama il metodo di avvio della fibra
- corhost-> CreateLogicalThreadState();
- eseguire il metodo principale di fibra astratta.
Infine la fibra deve cedere indietro alla fibra principale:
- corhost-> SwitchOutLogicalThreadState (& cookie);
- SwitchToFiber (fibra);
- corhost-> SwitchInLogicalThreadState (& cookie); // il biscotto principale in fibra , giusto?
Anche la fibra principale riprenderà una fibra preesistente:
- corhost-> SwitchOutLogicalThreadState (& cookie);
- SwitchToFiber (fibra);
- corhost-> SwitchInLogicalThreadState (& cookie); // il principale cookie in fibra, giusto?
Quanto segue è fiber.cpp che avvolge l'API della fibra per il codice gestito.
#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
}
}
Il file di classe fiber.cpp sopra è l'unica classe nel progetto C++ di Visaul. È costruito come una DLL con supporto CLR usando/CLR: switch oldstyle.
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();
}
}
}
}
La prova di unità di cui sopra dà il seguente risultato e poi si blocca male:
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.
E quale versione di C#/Fx stai usando? L'originale era già considerato instabile per Fx2 –
Questo è C# 3.5. Quindi è senza speranza? Cerchiamo disperatamente di capire come archiviare e riprendere lo stato del thread per una pianificazione ad alte prestazioni. Ho questa altra domanda che è più generale e spiega il background del perché abbiamo bisogno di questa capacità. Altre idee per soluzioni sono le benvenute! http://stackoverflow.com/questions/8685806/c-sharp-first-class-continuation-via-c-interop-or-some-other-way – Wayne
Un thread SO correlato con alternative: [Esiste una fibra api in .net?] (http://stackoverflow.com/questions/1949051/is-there-a-fiber-api-in-net) –