0

I am running a Delegate Async Callback pattern on a fibonacci method. This static method contains a loop where the thread sleeps for 300ms to print out the background Thread pool Id. Unfortunately, in my FibCompleted() the EndInvoke method is terminating my process. Any tips would be helpful. Thank you.

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);
    }
}
  • Wow, a lot to unpack here. I'm afraid there is almost nothing here that can be described as being correct...It looks like you are trying to learn to use threading and async, and are TRYING to use the APM model, but this isn't how to do the APM model at all. – Aron Mar 02 '21 at 16:09
  • This AMP model seems to follow most examples I have seen. The only difference is that I am writing the background thread from a loop within a method (which should be fine). It functions correctly when I don't use the `BeginInvoke` callback functionality. `EndInvoke` seems to work fine with no callbacks from Main(); however, it fails in the above implementation. – Erik Sven Broberg Mar 02 '21 at 16:45
  • 1
    Just don't use this style of asynchronous programming at all. Use Tasks and the TPL to write asynchronous programs. – Servy Mar 02 '21 at 16:54
  • I didn't realize this style was out of style. Taking a class on these topics and we are going old school first it seems. Either way, `EndInvoke` should be easily addressable, no? Microsoft posted **Calling Synchronous Methods Asynchronously** a few years ago that uses this style. – Erik Sven Broberg Mar 02 '21 at 17:04
  • @ErikSvenBroberg, its not just out of style. There are another couple of styles that have gone out of style on top of that, including EAP, AsyncEnumerator, RX.Net etc etc... I wouldn't use APM unless I found myself on a dotnet 2.0 platform. – Aron Mar 02 '21 at 17:09
  • @ErikSvenBroberg Don't use APM. [It isn't even supported in the latest dotnet.](https://stackoverflow.com/questions/45183294/begininvoke-not-supported-on-net-core-platformnotsupported-exception) – Aron Mar 02 '21 at 17:13
  • @Aron That's not making the pattern not supported, it's just making a particular method that happens to use that pattern not be supported. The pattern is just a pattern, it doesn't require language or runtime support. – Servy Mar 02 '21 at 17:16
  • @Servy True, but doesn't help. Technically what isn't supported it the conversion of a Sync method into TAP using BeginInvoke/EndInvoke. In practice, I doubt the OP knows about difference between Threadless Interupt based async and ThreadPool blocking async, so I am lying to him, to try to steer him towards better APIs. – Aron Mar 02 '21 at 17:23
  • @Aron Why lie when there are so many *good* and *truthful* reasons to use a better pattern? Lying just causes confusion and weakens your own position when they inevitably see some code using the pattern that you claim doesn't exist. – Servy Mar 02 '21 at 17:26
  • @Servy, your teachers did it. For example, centrafugal force, gravity, classical synchronized common time etc etc... – Aron Mar 02 '21 at 17:36
  • @Aron There's a difference between oversimplifying a complex topic (and clearly stating it's a simplification) when discussing it an introductory level (something which I would also argue should be avoided as much as possible) and just stating something that's factually inaccurate and also easily shown to be false. By all means, oversimplify and say they shouldn't use the pattern, the problem is you attempting to justify that position with an argument that's false and not convincing as a result. – Servy Mar 02 '21 at 17:41

1 Answers1

0

If you must use pure APM. Here is an example of it in action.

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

However. Nobody uses pure APM any more. APM was superceeded by EAP, which was in turn superceeded by TAP.

I would advise you learn TAP instead. It is much simplier. To adapt APM code to TAP you can use the TaskFactory helper function.

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

However, most people would just work in TAP. Which would look like this.

 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
  • 15,464
  • 3
  • 31
  • 64
  • Thank you to everyone who chimed in. I am quite new to Async models and am taking a course that is apparently teaching the old school methods. @Aron - What you have posted is extremely helpful and simplifies what I was attempting. I need to do a bit more study on these techniques, but can't see why I wouldn't use TAP in the future. – Erik Sven Broberg Mar 02 '21 at 21:46