Задача на многопоточность. Полусинхронный вызов делегата

Задача В .net есть возможность звать делегаты как синхронно:

EventHandler h = new EventHandler(this.myEventHandler); 
h.Invoke(null, EventArgs.Empty); 
так и асинхронно:
var res = h.BeginInvoke(null, EventArgs.Empty, null, null);

Нужно реализовать возможность полусинхронного вызова делегата (написать реализацию класса AsyncCaller), который бы работал таким образом:

EventHandler h = new EventHandler(this.myEventHandler); 
ac = new AsyncCaller(h); 
bool completedOK = ac.Invoke(5000, null, EventArgs.Empty);

Полусинхронного в данном случае означает, что делегат будет вызван, и вызывающий поток будет ждать, пока вызов не выполнится. Но если выполнение делегата займет больше 5000 миллисекунд, то ac.Invoke выйдет и вернет в completedOK значение false.

Подскажите как лучше делать, или что посмотреть.


Ответы (1 шт):

Автор решения: aepot

Вы просите странного, Delegate.BeginInvoke не поддерживается со времен .NET Core 2.0, то есть достаточно давно. EAP устарел, позвольте ему покоиться с миром. Пора бы уже изучать TAP.

Но если очень хочется, то почему бы и нет.

class Program
{
    private void MyEventHandler(object sender, EventArgs e)
    {
        Console.WriteLine("Enter Handler");
        Thread.Sleep(1000);
        Console.WriteLine("Exit Handler");
    }

    private void Run()
    {
        EventHandler h = new EventHandler(MyEventHandler);
        AsyncCaller ac = new AsyncCaller(h);
        if (ac.Invoke(5000, this, EventArgs.Empty))
            Console.WriteLine("Completed successfully");
        else
            Console.WriteLine("Timeout occured");
    }

    static void Main(string[] args)
    {
        new Program().Run();
        Console.WriteLine("Done.");
        Console.ReadKey();
    }
}

public class AsyncCaller
{
    private readonly EventHandler handler;
    private Thread thread;

    public AsyncCaller(EventHandler handler)
    {
        this.handler = handler;
    }

    private void Aborter(IAsyncResult ar)
    {
        thread?.Abort();
    }

    private void Wait(object timeout)
    {
        Thread.Sleep((int)timeout);
    }

    public bool Invoke(int timeout, object sender, EventArgs e)
    {
        thread = new Thread(Wait);
        IAsyncResult asyncResult = handler?.BeginInvoke(sender, e, Aborter, this);
        thread.Start(timeout);
        thread.Join();
        thread = null;
        return asyncResult.IsCompleted;
    }
}

Запускаем делегат, который работает секунду, получаем:

Enter Handler
Exit Handler
Completed successfully
Done.

Меняем 1000 на 6000, получаем

Enter Handler
Timeout occured
Done.
Exit Handler

А теперь то же самое, только с использованием TAP

private static async Task HandleAsync()
{
    Console.WriteLine("Enter Handler");
    await Task.Delay(1000);
    Console.WriteLine("Exit Handler");
}

static async Task Main(string[] args)
{
    Task t = HandleAsync();
    if (await Task.WhenAny(t, Task.Delay(5000)) == t)
        Console.WriteLine("Completed successfully");
    else
        Console.WriteLine("Timeout occured");
    Console.WriteLine("Done.");
    Console.ReadKey();
}

Вывод при аналогичных ожиданиях такой же.


Обе реализации не идеальны и я бы допилил в сторону усложнения и более оптимальной работы с потоками, но оставил чем проще - тем лучше, главное чтобы работало.

→ Ссылка