Тупик при объединении удаленных приложений и задач приложения - программирование
Подтвердить что ты не робот

Тупик при объединении удаленных приложений и задач приложения

Мое приложение должно загружать плагины в отдельные домены приложений, а затем выполнять некоторый код внутри них асинхронно. Я написал код для упаковки Task в маршируемых типах:

static class RemoteTask
{
    public static async Task<T> ClientComplete<T>(RemoteTask<T> remoteTask,
                                                  CancellationToken cancellationToken)
    {
        T result;

        using (cancellationToken.Register(remoteTask.Cancel))
        {
            RemoteTaskCompletionSource<T> tcs = new RemoteTaskCompletionSource<T>();
            remoteTask.Complete(tcs);
            result = await tcs.Task;
        }

        await Task.Yield(); // HACK!!

        return result;
    }

    public static RemoteTask<T> ServerStart<T>(Func<CancellationToken, Task<T>> func)
    {
        return new RemoteTask<T>(func);
    }
}

class RemoteTask<T> : MarshalByRefObject
{
    readonly CancellationTokenSource cts = new CancellationTokenSource();
    readonly Task<T> task;

    internal RemoteTask(Func<CancellationToken, Task<T>> starter)
    {
        this.task = starter(cts.Token);
    }

    internal void Complete(RemoteTaskCompletionSource<T> tcs)
    {
        task.ContinueWith(t =>
        {
            if (t.IsFaulted)
            {
                tcs.TrySetException(t.Exception);
            }
            else if (t.IsCanceled)
            {
                tcs.TrySetCancelled();
            }
            else
            {
                tcs.TrySetResult(t.Result);
            }
        }, TaskContinuationOptions.ExecuteSynchronously);
    }

    internal void Cancel()
    {
        cts.Cancel();
    }
}

class RemoteTaskCompletionSource<T> : MarshalByRefObject
{
    readonly TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();

    public bool TrySetResult(T result) { return tcs.TrySetResult(result); }
    public bool TrySetCancelled() { return tcs.TrySetCanceled(); }
    public bool TrySetException(Exception ex) { return tcs.TrySetException(ex); }

    public Task<T> Task
    {
        get
        {
            return tcs.Task;
        }
    }
}

Он используется как:

sealed class ControllerAppDomain
{
    PluginAppDomain plugin;

    public Task<int> SomethingAsync()
    {
        return RemoteTask.ClientComplete(plugin.SomethingAsync(), CancellationToken.None);
    }
}

sealed class PluginAppDomain : MarshalByRefObject
{
    public RemoteTask<int> SomethingAsync()
    {
        return RemoteTask.ServerStart(async cts =>
        {
            cts.ThrowIfCancellationRequested();
            return 1;
        });
    }
}

Но я столкнулся с проблемой. Если вы посмотрите в ClientComplete, там вставлен Task.Yield(). Если я прокомментирую эту строку, ClientComplete никогда не вернется. Любые идеи?

4b9b3361

Ответ 1

Мое лучшее предположение заключается в том, что вы столкнулись с этими проблемами из-за метода async, который содержит ожидание, и это управляется с помощью ThreadPool, который может выделять некоторый переработанный поток.

Ссылка Лучшая практика для вызова ConfigureAwait для всего кода на стороне сервера

Собственно, просто делать это можно (поместить вас в другой поток). Как только ваш метод асинхронизации попадает ожидание, метод блокируется, но поток возвращается в поток бассейн. Когда метод готов к продолжению, любой поток вырван из пула потоков и используется для возобновления метода.

Попробуйте упорядочить код, сгенерировать потоки для исходных случаев и производительность последней.