【问题标题】:Async Delegate EndInvoke terminating loop earlier than expectedAsync Delegate EndInvoke 比预期提前终止循环
【发布时间】:2021-03-02 16:05:28
【问题描述】:

我在斐波那契方法上运行委托异步回调模式。此静态方法包含一个循环,其中线程休眠 300 毫秒以打印出后台线程池 ID。不幸的是,在我的 FibCompleted() 中,EndInvoke 方法正在终止我的进程。任何提示都会有所帮助。 谢谢。

public delegate int FibPointer(int x); // FibbonaciSequence() pointer

class Program
{
    static void Main(string[] args)
    {
        // fibonacci Length
        int fibLength = 8;

        // point delegate to method
        FibPointer fb = new FibPointer(FibonacciSequence);
        IAsyncResult iftAR = fb.BeginInvoke(
            fibLen, new AsyncCallback(FibCompleted), null);

        Console.WriteLine("Fibonacci process now running on thread {0}\n",
            Thread.CurrentThread.ManagedThreadId);

        int count = 0;
        while (!iftAR.IsCompleted) // completion occurs when userIN length is reached
        {
            // run fib sequence. 
            Console.WriteLine("{0}", FibonacciSequence(count));
            count++;
        }
        Console.ReadKey();
    }

    static int FibonacciSequence(int num)
    {
        int num1 = 0, num2 = 1, res = 0;

        if (num == 0) return 0;
        if (num == 1) return 1;

        for (int i = 0; i < num; i++)
        {
            res = num1 + num2;
            num1 = num2;
            num2 = res;

            Thread.Sleep(300);
            // track background thread from pool
            Console.WriteLine("Working on thread: {0}", 
                Thread.CurrentThread.ManagedThreadId);
        }
        return res;
    }

    static void FibCompleted(IAsyncResult ar)
    {
        Console.WriteLine("\nFib Sequence Completed.");

        // retrieve result 
        AsyncResult res = (AsyncResult)ar;
        //FibPointer fp = ar.AsyncState as FibPointer;
        FibPointer fp = res.AsyncDelegate as FibPointer;

        // call EndInvoke to grab results
        string returnVal = fp.EndInvoke(ar).ToString();
        Console.WriteLine("\nreturn val is: {0}", returnVal);
    }
}

【问题讨论】:

  • 哇,这里有很多东西要解压。恐怕这里几乎没有什么可以被描述为正确的......看起来你正在尝试学习使用线程和异步,并且正在尝试使用 APM 模型,但这不是怎么做APM 模型。
  • 这个 AMP 模型似乎遵循了我见过的大多数示例。唯一的区别是我正在从方法内的循环中编写后台线程(应该没问题)。当我不使用BeginInvoke 回调功能时,它可以正常工作。 EndInvoke 似乎工作正常,没有来自 Main() 的回调;但是,它在上述实现中失败了。
  • 根本不要使用这种异步编程风格。使用 Tasks 和 TPL 编写异步程序。
  • 我没有意识到这种风格已经过时了。上一堂关于这些主题的课,我们似乎首先要上老派。不管怎样,EndInvoke 应该很容易寻址,不是吗?微软几年前发布了异步调用同步方法,使用了这种风格。
  • @ErikSvenBroberg,它不仅仅是过时了。除此之外,还有另外几种风格已经过时,包括 EAP、AsyncEnumerator、RX.Net 等……除非我发现自己在 dotnet 2.0 平台上,否则我不会使用 APM。

标签: c# delegates


【解决方案1】:

如果您必须使用纯 APM。这是一个实际应用的示例。

void Main()
{
    // fibonacci Length
    int fibLength = 8;

    // point delegate to method
    Func<int, int> fb = FibonacciSequence;
    var completedEvent = new System.Threading.AutoResetEvent(false);
    var iftAR = fb.BeginInvoke(fibLength, FibCompleted, completedEvent);

    completedEvent.WaitOne();
    var result = fb.EndInvoke(iftAR);
    Console.WriteLine("Fibonacci process now running on thread {0}\n", Thread.CurrentThread.ManagedThreadId);
    Console.WriteLine(result);
}

static int FibonacciSequence(int num)
{
    int num1 = 0, num2 = 1, res = 0;

    if (num == 0) return 0;
    if (num == 1) return 1;

    for (int i = 0; i < num; i++)
    {
        res = num1 + num2;
        num1 = num2;
        num2 = res;

        Thread.Sleep(300);
        // track background thread from pool
        Console.WriteLine("Working on thread: {0}",
            Thread.CurrentThread.ManagedThreadId);
    }
    return res;
}

static void FibCompleted(IAsyncResult ar)
{
    var completedEvent = (AutoResetEvent)ar.AsyncState;
    completedEvent.Set();
}

但是。没有人再使用纯 APM。 APM 被 EAP 取代,EAP 又被 TAP 取代。

我建议你改为学习 TAP。它要简单得多。要使 APM 代码适应 TAP,您可以使用 TaskFactory 辅助函数。

async Task Main()
{
    // fibonacci Length
    int fibLength = 8;

    // point delegate to method
    Func<int, int> fb = FibonacciSequence;
    
    var result = await Task.Factory.FromAsync (fb.BeginInvoke, fb.EndInvoke, fibLength, null);
    
    Console.WriteLine("Fibonacci process now running on thread {0}\n", Thread.CurrentThread.ManagedThreadId);
    Console.WriteLine(result);
}

static int FibonacciSequence(int num)
{
    int num1 = 0, num2 = 1, res = 0;

    if (num == 0) return 0;
    if (num == 1) return 1;

    for (int i = 0; i < num; i++)
    {
        res = num1 + num2;
        num1 = num2;
        num2 = res;

        Thread.Sleep(300);
        // track background thread from pool
        Console.WriteLine("Working on thread: {0}",
            Thread.CurrentThread.ManagedThreadId);
    }
    return res;
}

但是,大多数人只会在 TAP 中工作。看起来像这样。

 async Task Main()
{
    // fibonacci Length
    int fibLength = 8;  
    var result = await FibonacciSequence(fibLength);
    
    Console.WriteLine("Fibonacci process now running on thread {0}\n", Thread.CurrentThread.ManagedThreadId);
    Console.WriteLine(result);
}

static Task<int> FibonacciSequence(int num)
{
    // Run computationally intensive work on thread pool.
    return Task.Run(() =>
    {
        int num1 = 0, num2 = 1, res = 0;

        if (num == 0) return 0;
        if (num == 1) return 1;

        for (int i = 0; i < num; i++)
        {
            res = num1 + num2;
            num1 = num2;
            num2 = res;

            Thread.Sleep(300);
            // track background thread from pool
            Console.WriteLine("Working on thread: {0}",
                Thread.CurrentThread.ManagedThreadId);
        }
        return res;
    });
}

【讨论】:

  • 感谢所有参与进来的人。我对异步模型很陌生,并且正在学习一门显然是在教授老派方法的课程。 @Aron - 您发布的内容非常有帮助并简化了我的尝试。我需要对这些技术做更多的研究,但不明白为什么我以后不使用 TAP。
猜你喜欢
  • 1970-01-01
  • 2021-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-04
  • 2022-01-20
  • 1970-01-01
相关资源
最近更新 更多