1

我正在尝试一个有趣的模式,使用 RX 扩展进行异常重试。我有以下代码。

Public Async Function InitializeCommunications() As Task
    Await Observable.Create(Of Unit)(Async Function(observer)
                                         MessageBox.Show("Please press reset")
                                         Await Me.Cockpit.LoadDriverToPLC()
                                         MessageBox.Show("Please press start")
                                         observer.OnNext(Unit.Default)
                                     End Function).Retry()
End Function

该代码几乎可以完美运行。如果在 LoadDriverToPLC 中抛出异常,则重新订阅序列并重新运行主体,并要求用户再次按下重置。

这段代码只有一个问题。第一次通过 MessageBox 调用是正确的模态。这意味着它们位于主窗口上方,您无法通过意外单击主窗口来隐藏它们。但是,如果抛出异常并重试正文,则 MessageBox 调用不再是模态的。

我确定这与 RX 时间表有关。确保身体始终在相同的上下文中运行的诀窍是什么?

我打算稍后将这种模式包装起来,当我可以使它可靠时,像

Public Async Function InitializeCommunications() As Task
    Await RetryOn(of Exception)(Async Sub()
                                         MessageBox.Show("Please press reset")
                                         Await Me.Cockpit.LoadDriverToPLC()
                                         MessageBox.Show("Please press start")
                                End Sub)
End Function
4

1 回答 1

1

解决方案似乎是捕获同步上下文并强制观察者收到正确上下文的通知,然后使用相同的上下文重试

Public Async Function InitializeCommunications(msgFeedback As Func(Of String, Task)) As Task
    Dim context = SynchronizationContext.Current
    Await Observable.Create(Of Unit)(
             Async Function(observer)
                 MessageBox.Show("Please press reset")
                 Await Me.Cockpit.LoadMessageLoopToPLC(111)
                 MessageBox.Show("Please press start")
                 observer.OnNext(Unit.Default)
             End Function).ObserveOn(context).Retry()
End Function

另一种可能更好的方法是像这样在 Object 上创建一些扩展方法

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FunctionalExtensions
{
    public static class ObjectMixins
    {
        public static T Retry<Ex, T>(this Object This, int count, Func<T> action)
        where  Ex : Exception
        {
            while (true)
            {
                try
                {
                    return action();
                }
                catch(Ex)
                {
                    if (count==0)
                    {
                        throw;
                    }
                    count--;
                }
            }
        }

        public static async Task<T> Retry<Ex, T>(this Object This, int count, Func<Task<T>> action)
        where  Ex : Exception
        {
            while (true)
            {
                try
                {
                    return await action();
                }
                catch(Ex)
                {
                    if (count==0)
                    {
                        throw;
                    }
                    count--;
                }
            }
        }

        public static void Retry<Ex>(this Object This, int count, Action action)
        where  Ex : Exception
        {
            This.Retry<Ex, bool>(count, () => { action(); return true; });
        }

        public static async Task Retry<Ex>(this Object This, int count, Func<Task> action)
        where  Ex : Exception
        {
            await This.Retry<Ex, bool>(count, async () => { await action(); return true; });
        }

        public static void Retry<Ex>(this Object This, Action action)
        where  Ex : Exception
        {
            This.Retry<Ex, bool>(() => { action(); return true; });
        }

        public static T Retry<Ex, T>(this Object This, Func<T> action)
        where Ex : Exception
        {
            while (true)
            {
                try
                {
                    return action();
                }
                catch(Ex)
                {
                }
            }
        }

    }
}

它支持同步和异步操作。您可以像下面这样使用它在抛出异常之前重试 10 次

Public Async Function InitializeCommunications(msgFeedback As Func(Of String, Task)) As Task
    Await Retry(Of Exception)(10, 
        Async Function()
          MessageBox.Show("Please press reset on the NUM control unit then press ok here.")
          Await Me.Cockpit.LoadMessageLoopToPLC(111)
          MessageBox.Show("Please press start on control unit.")
       End Function)
End Function
于 2012-11-20T07:36:56.213 回答