ChatGPT解决这个技术问题 Extra ChatGPT

我有一个 async 方法:

public async Task<string> GenerateCodeAsync()
{
    string code = await GenerateCodeService.GenerateCodeAsync();
    return code;
}

我需要从同步方法中调用此方法。

我怎样才能做到这一点而不必复制 GenerateCodeAsync 方法以使其同步工作?

更新

然而没有找到合理的解决方案。

但是,我看到 HttpClient 已经实现了这个模式

using (HttpClient client = new HttpClient())
{
    // async
    HttpResponseMessage responseAsync = await client.GetAsync(url);

    // sync
    HttpResponseMessage responseSync = client.GetAsync(url).Result;
}
我希望有一个更简单的解决方案,认为 asp.net 处理这比编写这么多行代码要容易得多
为什么不直接采用异步代码?理想情况下,您需要更多的异步代码,而不是更少。
[为什么不直接采用异步代码?] 哈,可能正是因为人们正在接受异步代码,所以他们需要这个解决方案,因为项目的大部分都被转换了!你不可能在一天之内重建罗马。
@NicholasPetersen 有时第 3 方库会强迫您这样做。在 FluentValidation 之外的 WithMessage 方法中构建动态消息的示例。由于库设计,没有用于此的异步 API - WithMessage 重载是静态的。将动态参数传递给 WithMessage 的其他方法很奇怪。

R
Reyan Chougle

您可以访问任务的 Result 属性,这将导致您的线程阻塞,直到结果可用:

string code = GenerateCodeAsync().Result;

注意:在某些情况下,这可能会导致死锁:您对 Result 的调用会阻塞主线程,从而阻止异步代码的其余部分执行。您有以下选项来确保不会发生这种情况:

将 .ConfigureAwait(false) 添加到您的库方法或

在线程池线程中显式执行您的异步方法并等待它完成: string code = Task.Run(() => GenerateCodeAsync).Result;

并不意味着您应该在所有异步调用之后无意识地添加 .ConfigureAwait(false)!有关为何以及何时应使用 .ConfigureAwait(false) 的详细分析,请参阅以下博客文章:

.NET 博客:ConfigureAwait 常见问题解答


如果调用 result 有死锁的风险,那么何时是否可以安全地获得结果?每个异步调用都需要 Task.Run 还是 ConfigureAwait(false)
ASP.NET 中没有“主线程”(与 GUI 应用程序不同),但由于 how AspNetSynchronizationContext.Post 序列化异步延续:Task newTask = _lastScheduledTask.ContinueWith(_ => SafeWrapCallback(action)); _lastScheduledTask = newTask;
@RobertHarvey:如果您无法控制要阻止的异步方法的实现,那么是的,您应该用 Task.Run 包装它以确保安全。或者使用 WithNoContext 之类的东西来减少冗余线程切换。
注意:如果调用者本身在线程池中,调用 .Result 仍然会死锁。假设线程池大小为 32,并且有 32 个任务正在运行,Wait()/Result 正在等待一个尚未计划的第 33 个任务,该任务希望在其中一个等待线程上运行。
@Warty - 澄清一下,为了发生死锁,线程池必须重新使用等待结果的同一个线程。这种情况很少发生——这很糟糕,因为当它发生时,哪里出了问题就不是很明显了。
D
Diego Torres

您应该获取等待程序 (GetAwaiter()) 并结束等待以等待异步任务 (GetResult()) 的完成。

string code = GenerateCodeAsync().GetAwaiter().GetResult();

我们在使用这个解决方案时遇到了死锁。被警告。
MSDN Task.GetAwaiter:此方法旨在供编译器使用,而不是在应用程序代码中使用。
我仍然收到错误对话框弹出窗口(违背我的意愿),带有“切换到”或“重试”按钮......。但是,调用实际上会执行并返回正确的响应。
@Oliver,我刚刚看到这篇文章,想知道为什么我在 WebForms 中的应用程序在我有上面的代码时会死锁。这确实会导致死锁。
R
Romano Zumbé

您应该能够使用委托、lambda 表达式完成此操作

private void button2_Click(object sender, EventArgs e)
    {

        label1.Text = "waiting....";

        Task<string> sCode = Task.Run(async () =>
        {
            string msg =await GenerateCodeAsync();
            return msg;
        });

        label1.Text += sCode.Result;

    }

    private Task<string> GenerateCodeAsync()
    {
        return Task.Run<string>(() => GenerateCode());
    }

    private string GenerateCode()
    {
        Thread.Sleep(2000);
        return "I m back" ;
    }

此代码段将无法编译。 Task.Run 的返回类型是 Task。有关完整说明,请参阅此 MSDN blog
感谢您指出,是的,它返回任务类型。将“string sCode”替换为 Task 或 var sCode 应该可以解决它。为方便起见,添加完整的编译代码。
恕我直言,使这更有可能成功的本质是使用 Task.Run 包装调用 - 从而将其移动到线程池。但是,根据其他答案和评论,这并不能确保它永远不会死锁——它可能只会使死锁“罕见”——因此更难找出问题所在。
C
Community

我需要从同步方法中调用此方法。

正如另一个答案所暗示的,使用 GenerateCodeAsync().ResultGenerateCodeAsync().Wait() 是可能的。这将阻塞当前线程,直到 GenerateCodeAsync 完成。

但是,您的问题被标记为 ,并且您还留下了评论:

我希望有一个更简单的解决方案,认为 asp.net 处理这比编写这么多行代码要容易得多

我的观点是,您不应该阻塞 ASP.NET 中的异步方法。这将降低您的网络应用程序的可伸缩性,并可能造成死锁(当 GenerateCodeAsync 内的 await 延续发布到 AspNetSynchronizationContext 时)。使用 Task.Run(...).Result 将某些内容卸载到池线程然后阻塞将进一步损害可伸缩性,因为它会导致 +1 线程来处理给定的 HTTP 请求。

ASP.NET 具有对异步方法的内置支持,可以通过异步控制器(在 ASP.NET MVC 和 Web API 中)或直接通过经典 ASP.NET 中的 AsyncManagerPageAsyncTask。你应该使用它。有关详细信息,请查看 this answer


我正在覆盖 DbContextSaveChanges() 方法,在这里我正在调用异步方法,所以不幸的是异步控制器在这种情况下无法帮助我
@RaraituL,一般来说,你不要混合异步和同步代码,选择euther模型。您可以同时实现 SaveChangesAsyncSaveChanges,只要确保它们不会在同一个 ASP.NET 项目中同时被调用。
并非所有的 .NET MVC 过滤器都支持异步代码,例如 IAuthorizationFilter,所以我不能一直使用 async
@Noseratio 这是一个不切实际的目标。具有异步和同步代码的库太多,以及无法仅使用一种模型的情况。例如,MVC ActionFilters 不支持异步代码。
@Noserato,问题是关于从同步调用异步方法。有时您无法更改您实现的 API。假设您从某个 3-rd 方框架“A”实现了一些同步接口(您不能将框架重写为异步方式),但是您尝试在实现中使用的 3rd 方库“B”只有异步。生成的产品也是库,可以在任何地方使用,包括 ASP.NET 等。
V
Vitaliy Markitanov

Microsoft Identity 具有同步调用异步方法的扩展方法。例如有 GenerateUserIdentityAsync() 方法和等于 CreateIdentity()

如果您查看 UserManagerExtensions.CreateIdentity() 它看起来像这样:

 public static ClaimsIdentity CreateIdentity<TUser, TKey>(this UserManager<TUser, TKey> manager, TUser user,
        string authenticationType)
        where TKey : IEquatable<TKey>
        where TUser : class, IUser<TKey>
    {
        if (manager == null)
        {
            throw new ArgumentNullException("manager");
        }
        return AsyncHelper.RunSync(() => manager.CreateIdentityAsync(user, authenticationType));
    }

现在让我们看看 AsyncHelper.RunSync 做了什么

  public static TResult RunSync<TResult>(Func<Task<TResult>> func)
    {
        var cultureUi = CultureInfo.CurrentUICulture;
        var culture = CultureInfo.CurrentCulture;
        return _myTaskFactory.StartNew(() =>
        {
            Thread.CurrentThread.CurrentCulture = culture;
            Thread.CurrentThread.CurrentUICulture = cultureUi;
            return func();
        }).Unwrap().GetAwaiter().GetResult();
    }

所以,这是你的异步方法的包装器。并且请不要从 Result 读取数据 - 它可能会阻止您在 ASP 中的代码。

还有另一种方式 - 这对我来说很可疑,但你也可以考虑一下

  Result r = null;

            YourAsyncMethod()
                .ContinueWith(t =>
                {
                    r = t.Result;
                })
                .Wait();

您认为您建议的第二种方式存在什么问题?
@DavidClarke 可能是在没有锁的情况下从多个线程访问非易失性变量的线程安全问题。
O
Ogglas

为了防止死锁,当我必须同步调用 @Heinzi 提到的异步方法时,我总是尝试使用 Task.Run()

但是,如果异步方法使用参数,则必须修改该方法。例如 Task.Run(GenerateCodeAsync("test")).Result 给出错误:

参数 1:无法从 'System.Threading.Tasks.Task' 转换为 'System.Action'

这可以这样调用:

string code = Task.Run(() => GenerateCodeAsync("test")).Result;

s
soccer7

该线程上的大多数答案要么很复杂,要么会导致死锁。

下面的方法很简单,它会避免死锁,因为我们正在等待任务完成,然后才得到它的结果——

var task = Task.Run(() => GenerateCodeAsync()); 
task.Wait();
string code = task.Result;

此外,这是对 MSDN 文章的参考,它谈到了完全相同的事情 - https://blogs.msdn.microsoft.com/jpsanders/2017/08/28/asp-net-do-not-use-task-result-in-main-context/


尽管有 Microsoft 参考,但它的 Task.Run 使这项工作(在 ASP.NET 上下文线程上运行时)。 Wait 无关紧要 - Result 在任务未完成时执行等效操作。请参阅this SO Q&A
@ToolmakerSteve 我不同意你的看法。我看了this reference,两者之间的代码不一样。直接从任务中提取 .Result 可能会随机导致死锁。我知道,我遇到了这个问题。当它发生时很难排除故障。
我不得不说,当您无法使调用方法异步时,这是最正确的解决方案。最好的答案是使整个流程异步。
@FrankThomas - 查看 stackoverflow.com/a/35948021/199364 中提到的源参考,Task.Wait 测试 IsWaitNotificationEnabledOrNotRanToCompletion,然后调用 InternalWaitFuture.Result 测试 IsWaitNotificationEnabledOrNotRanToCompletion,然后调用 GetResultCore,它执行 if (!IsCompleted) InternalWait。我看不出有什么区别,死锁的可能性。当然,不可能证明没有死锁,并且代码中的任何更改都会更改时间,因此完全有可能让一种方法随机失败,而另一种方法可以工作......直到它没有。
(我的意思是不可能证明缺席,除非可以通过设计或静态分析证明缺席;我的观点是,鉴于所示的实现,您无法确定 Wait 不会导致尽管 Result 会导致。)
J
Jiří Herník

好吧,我正在使用这种方法,它还将处理和传播来自底层异步任务的异常。

    private string RunSync()
    {
        var task = Task.Run(async () => await GenerateCodeService.GenerateCodeAsync());
        if (task.IsFaulted && task.Exception != null)
        {
            throw task.Exception;
        }

        return task.Result;
    }

P
Paul

一些扩展方法异步等待异步操作完成,然后设置一个ManualResetEvent来指示完成。

注意:您可以使用 Task.Run(),但是扩展方法是一个更简洁的接口,可以表达您真正想要的内容。

显示如何使用扩展的测试:

    [TestClass]
    public class TaskExtensionsTests
    {
        [TestMethod]
        public void AsynchronousOperationWithNoResult()
        {
            SampleAsynchronousOperationWithNoResult().AwaitResult();
        }

        [TestMethod]
        public void AsynchronousOperationWithResult()
        {
            Assert.AreEqual(3, SampleAsynchronousOperationWithResult(3).AwaitResult());
        }

        [TestMethod]
        [ExpectedException(typeof(Exception))]
        public void AsynchronousOperationWithNoResultThrows()
        {
            SampleAsynchronousOperationWithNoResultThrows().AwaitResult();
        }

        [TestMethod]
        [ExpectedException(typeof(Exception))]
        public void AsynchronousOperationWithResultThrows()
        {
            SampleAsynchronousOperationWithResultThrows(3).AwaitResult();
        }

        private static async Task SampleAsynchronousOperationWithNoResult()
        {
            await Task.Yield();
        }

        private static async Task<T> SampleAsynchronousOperationWithResult<T>(T result)
        {
            await Task.Yield();
            return result;
        }

        private static async Task SampleAsynchronousOperationWithNoResultThrows()
        {
            await Task.Yield();
            throw new Exception();
        }

        private static async Task<T> SampleAsynchronousOperationWithResultThrows<T>(T result)
        {
            await Task.Yield();
            throw new Exception();
        }

        [TestMethod]
        public void AsynchronousValueOperationWithNoResult()
        {
            SampleAsynchronousValueOperationWithNoResult().AwaitResult();
        }

        [TestMethod]
        public void AsynchronousValueOperationWithResult()
        {
            Assert.AreEqual(3, SampleAsynchronousValueOperationWithResult(3).AwaitResult());
        }

        [TestMethod]
        [ExpectedException(typeof(Exception))]
        public void AsynchronousValueOperationWithNoResultThrows()
        {
            SampleAsynchronousValueOperationWithNoResultThrows().AwaitResult();
        }

        [TestMethod]
        [ExpectedException(typeof(Exception))]
        public void AsynchronousValueOperationWithResultThrows()
        {
            SampleAsynchronousValueOperationWithResultThrows(3).AwaitResult();
        }

        private static async ValueTask SampleAsynchronousValueOperationWithNoResult()
        {
            await Task.Yield();
        }

        private static async ValueTask<T> SampleAsynchronousValueOperationWithResult<T>(T result)
        {
            await Task.Yield();
            return result;
        }

        private static async ValueTask SampleAsynchronousValueOperationWithNoResultThrows()
        {
            await Task.Yield();
            throw new Exception();
        }

        private static async ValueTask<T> SampleAsynchronousValueOperationWithResultThrows<T>(T result)
        {
            await Task.Yield();
            throw new Exception();
        }
    }

扩展名

    /// <summary>
    /// Defines extension methods for <see cref="Task"/> and <see cref="ValueTask"/>.
    /// </summary>
    public static class TaskExtensions
    {
        /// <summary>
        /// Synchronously await the results of an asynchronous operation without deadlocking; ignoring cancellation.
        /// </summary>
        /// <param name="task">
        /// The <see cref="Task"/> representing the pending operation.
        /// </param>
        public static void AwaitCompletion(this ValueTask task)
        {
            new SynchronousAwaiter(task, true).GetResult();
        }

        /// <summary>
        /// Synchronously await the results of an asynchronous operation without deadlocking; ignoring cancellation.
        /// </summary>
        /// <param name="task">
        /// The <see cref="Task"/> representing the pending operation.
        /// </param>
        public static void AwaitCompletion(this Task task)
        {
            new SynchronousAwaiter(task, true).GetResult();
        }

        /// <summary>
        /// Synchronously await the results of an asynchronous operation without deadlocking.
        /// </summary>
        /// <param name="task">
        /// The <see cref="Task"/> representing the pending operation.
        /// </param>
        /// <typeparam name="T">
        /// The result type of the operation.
        /// </typeparam>
        /// <returns>
        /// The result of the operation.
        /// </returns>
        public static T AwaitResult<T>(this Task<T> task)
        {
            return new SynchronousAwaiter<T>(task).GetResult();
        }

        /// <summary>
        /// Synchronously await the results of an asynchronous operation without deadlocking.
        /// </summary>
        /// <param name="task">
        /// The <see cref="Task"/> representing the pending operation.
        /// </param>
        public static void AwaitResult(this Task task)
        {
            new SynchronousAwaiter(task).GetResult();
        }

        /// <summary>
        /// Synchronously await the results of an asynchronous operation without deadlocking.
        /// </summary>
        /// <param name="task">
        /// The <see cref="ValueTask"/> representing the pending operation.
        /// </param>
        /// <typeparam name="T">
        /// The result type of the operation.
        /// </typeparam>
        /// <returns>
        /// The result of the operation.
        /// </returns>
        public static T AwaitResult<T>(this ValueTask<T> task)
        {
            return new SynchronousAwaiter<T>(task).GetResult();
        }

        /// <summary>
        /// Synchronously await the results of an asynchronous operation without deadlocking.
        /// </summary>
        /// <param name="task">
        /// The <see cref="ValueTask"/> representing the pending operation.
        /// </param>
        public static void AwaitResult(this ValueTask task)
        {
            new SynchronousAwaiter(task).GetResult();
        }

        /// <summary>
        /// Ignore the <see cref="OperationCanceledException"/> if the operation is cancelled.
        /// </summary>
        /// <param name="task">
        /// The <see cref="Task"/> representing the asynchronous operation whose cancellation is to be ignored.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/> representing the asynchronous operation whose cancellation is ignored.
        /// </returns>
        public static async Task IgnoreCancellationResult(this Task task)
        {
            try
            {
                await task.ConfigureAwait(false);
            }
            catch (OperationCanceledException)
            {
            }
        }

        /// <summary>
        /// Ignore the <see cref="OperationCanceledException"/> if the operation is cancelled.
        /// </summary>
        /// <param name="task">
        /// The <see cref="ValueTask"/> representing the asynchronous operation whose cancellation is to be ignored.
        /// </param>
        /// <returns>
        /// The <see cref="ValueTask"/> representing the asynchronous operation whose cancellation is ignored.
        /// </returns>
        public static async ValueTask IgnoreCancellationResult(this ValueTask task)
        {
            try
            {
                await task.ConfigureAwait(false);
            }
            catch (OperationCanceledException)
            {
            }
        }

        /// <summary>
        /// Ignore the results of an asynchronous operation allowing it to run and die silently in the background.
        /// </summary>
        /// <param name="task">
        /// The <see cref="Task"/> representing the asynchronous operation whose results are to be ignored.
        /// </param>
        public static async void IgnoreResult(this Task task)
        {
            try
            {
                await task.ConfigureAwait(false);
            }
            catch
            {
                // ignore exceptions
            }
        }

        /// <summary>
        /// Ignore the results of an asynchronous operation allowing it to run and die silently in the background.
        /// </summary>
        /// <param name="task">
        /// The <see cref="ValueTask"/> representing the asynchronous operation whose results are to be ignored.
        /// </param>
        public static async void IgnoreResult(this ValueTask task)
        {
            try
            {
                await task.ConfigureAwait(false);
            }
            catch
            {
                // ignore exceptions
            }
        }
    }

    /// <summary>
    /// Internal class for waiting for asynchronous operations that have a result.
    /// </summary>
    /// <typeparam name="TResult">
    /// The result type.
    /// </typeparam>
    public class SynchronousAwaiter<TResult>
    {
        /// <summary>
        /// The manual reset event signaling completion.
        /// </summary>
        private readonly ManualResetEvent manualResetEvent;

        /// <summary>
        /// The exception thrown by the asynchronous operation.
        /// </summary>
        private Exception exception;

        /// <summary>
        /// The result of the asynchronous operation.
        /// </summary>
        private TResult result;

        /// <summary>
        /// Initializes a new instance of the <see cref="SynchronousAwaiter{TResult}"/> class.
        /// </summary>
        /// <param name="task">
        /// The task representing an asynchronous operation.
        /// </param>
        public SynchronousAwaiter(Task<TResult> task)
        {
            this.manualResetEvent = new ManualResetEvent(false);
            this.WaitFor(task);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SynchronousAwaiter{TResult}"/> class.
        /// </summary>
        /// <param name="task">
        /// The task representing an asynchronous operation.
        /// </param>
        public SynchronousAwaiter(ValueTask<TResult> task)
        {
            this.manualResetEvent = new ManualResetEvent(false);
            this.WaitFor(task);
        }

        /// <summary>
        /// Gets a value indicating whether the operation is complete.
        /// </summary>
        public bool IsComplete => this.manualResetEvent.WaitOne(0);

        /// <summary>
        /// Synchronously get the result of an asynchronous operation.
        /// </summary>
        /// <returns>
        /// The result of the asynchronous operation.
        /// </returns>
        public TResult GetResult()
        {
            this.manualResetEvent.WaitOne();
            return this.exception != null ? throw this.exception : this.result;
        }

        /// <summary>
        /// Tries to synchronously get the result of an asynchronous operation.
        /// </summary>
        /// <param name="operationResult">
        /// The result of the operation.
        /// </param>
        /// <returns>
        /// The result of the asynchronous operation.
        /// </returns>
        public bool TryGetResult(out TResult operationResult)
        {
            if (this.IsComplete)
            {
                operationResult = this.exception != null ? throw this.exception : this.result;
                return true;
            }

            operationResult = default;
            return false;
        }

        /// <summary>
        /// Background "thread" which waits for the specified asynchronous operation to complete.
        /// </summary>
        /// <param name="task">
        /// The task.
        /// </param>
        private async void WaitFor(Task<TResult> task)
        {
            try
            {
                this.result = await task.ConfigureAwait(false);
            }
            catch (Exception exception)
            {
                this.exception = exception;
            }
            finally
            {
                this.manualResetEvent.Set();
            }
        }

        /// <summary>
        /// Background "thread" which waits for the specified asynchronous operation to complete.
        /// </summary>
        /// <param name="task">
        /// The task.
        /// </param>
        private async void WaitFor(ValueTask<TResult> task)
        {
            try
            {
                this.result = await task.ConfigureAwait(false);
            }
            catch (Exception exception)
            {
                this.exception = exception;
            }
            finally
            {
                this.manualResetEvent.Set();
            }
        }
    }

    /// <summary>
    /// Internal class for  waiting for  asynchronous operations that have no result.
    /// </summary>
    public class SynchronousAwaiter
    {
        /// <summary>
        /// The manual reset event signaling completion.
        /// </summary>
        private readonly ManualResetEvent manualResetEvent = new ManualResetEvent(false);

        /// <summary>
        /// The exception thrown by the asynchronous operation.
        /// </summary>
        private Exception exception;

        /// <summary>
        /// Initializes a new instance of the <see cref="SynchronousAwaiter{TResult}"/> class.
        /// </summary>
        /// <param name="task">
        /// The task representing an asynchronous operation.
        /// </param>
        /// <param name="ignoreCancellation">
        /// Indicates whether to ignore cancellation. Default is false.
        /// </param>
        public SynchronousAwaiter(Task task, bool ignoreCancellation = false)
        {
            this.manualResetEvent = new ManualResetEvent(false);
            this.WaitFor(task, ignoreCancellation);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SynchronousAwaiter{TResult}"/> class.
        /// </summary>
        /// <param name="task">
        /// The task representing an asynchronous operation.
        /// </param>
        /// <param name="ignoreCancellation">
        /// Indicates whether to ignore cancellation. Default is false.
        /// </param>
        public SynchronousAwaiter(ValueTask task, bool ignoreCancellation = false)
        {
            this.manualResetEvent = new ManualResetEvent(false);
            this.WaitFor(task, ignoreCancellation);
        }

        /// <summary>
        /// Gets a value indicating whether the operation is complete.
        /// </summary>
        public bool IsComplete => this.manualResetEvent.WaitOne(0);

        /// <summary>
        /// Synchronously get the result of an asynchronous operation.
        /// </summary>
        public void GetResult()
        {
            this.manualResetEvent.WaitOne();
            if (this.exception != null)
            {
                throw this.exception;
            }
        }

        /// <summary>
        /// Background "thread" which waits for the specified asynchronous operation to complete.
        /// </summary>
        /// <param name="task">
        /// The task.
        /// </param>
        /// <param name="ignoreCancellation">
        /// Indicates whether to ignore cancellation. Default is false.
        /// </param>
        private async void WaitFor(Task task, bool ignoreCancellation)
        {
            try
            {
                await task.ConfigureAwait(false);
            }
            catch (OperationCanceledException)
            {
            }
            catch (Exception exception)
            {
                this.exception = exception;
            }
            finally
            {
                this.manualResetEvent.Set();
            }
        }

        /// <summary>
        /// Background "thread" which waits for the specified asynchronous operation to complete.
        /// </summary>
        /// <param name="task">
        ///     The task.
        /// </param>
        /// <param name="ignoreCancellation">
        /// Indicates whether to ignore cancellation. Default is false.
        /// </param>
        private async void WaitFor(ValueTask task, bool ignoreCancellation)
        {
            try
            {
                await task.ConfigureAwait(false);
            }
            catch (OperationCanceledException)
            {
            }
            catch (Exception exception)
            {
                this.exception = exception;
            }
            finally
            {
                this.manualResetEvent.Set();
            }
        }
    }
}

A
Avi Tshuva

编辑:

Task 有 Wait 方法,Task.Wait(),它等待“promise”解决然后继续,从而使其同步。例子:


async Task<String> MyAsyncMethod() { ... }

String mySyncMethod() {

    return MyAsyncMethod().Wait();
}

请详细说明您的答案。它是如何使用的?它对回答问题有多大帮助?
Z
Zibri

我更喜欢非阻塞方法:

            Dim aw1=GenerateCodeAsync().GetAwaiter()
            While Not aw1.IsCompleted
                Application.DoEvents()
            End While

d
dush88c

如果您有一个名为“ RefreshList ”的异步方法,则可以从如下所示的非异步方法调用该异步方法。

Task.Run(async () => { await RefreshList(); });