Xamarin 应用程序进入后台后 Lazy 初始化的单例会发生什么

Xamarin What happens to Lazy initialized singleton after App goes to background

我在 Xamarin.Forms 中对应用单例使用延迟初始化(应用在 iOS 上运行):

public sealed class DataSingleton
{
    private static readonly Lazy<DataSingleton> lazy = new Lazy<DataSingleton>(() => new DataSingleton(), LazyThreadSafetyMode.PublicationOnly); // tried withou 2nd parameter as well

    public static DataSingleton Instance
    {
        get { return lazy.Value; }
    }
    ...
}

我在网络服务器中调用它,它运行为 Angular 中的前端提供数据(使用网络视图显示 Angular 代码)

var server = new WebServer(o => o
                .WithUrlPrefix($"http://localhost:{appSettings.ApiPort}")
                .WithMode(HttpListenerMode.EmbedIO))
            .WithCors()
            .WithLocalSessionManager()
            .WithWebApi("/api", m => m
                .WithController<TestController>()
                .WithController<SettingsController>()
                .WithController<ReportController>()
                .WithController<SystemController>())
            .WithModule(new ActionModule("/", HttpVerbs.Any,
                ctx => ctx.SendDataAsync(new { Message = "Error" })))
            .RunAsync();

在controllers中调用DataSingleton来get/set数据,但是app returns从后台调用后,DataSingleton.Instance为null。

单例在后台短时间(约5分钟)不丢失数据怎么办

Update - I've figured out that this problem is only in Controllers, cause when app gets back to front I can see all the data in AppDelegate WillEnterForeground event..

在这种情况下,可能存在竞争条件。 如果两个(或多个线程)第一次同时读取 Instance,则会创建多个 DataSingleton 实例。然而,每隔一次读取只会得到一个实例。就看你的场景了,没问题。

public sealed class DataSingleton {
  private static instance;

  // will assign new DataSingleton only if "instance" is null.
  public static Instance => instance ??= new DataSingleton();
}

或者您可以使用 Interlocked class 确保,如果另一个线程已经初始化了 instance 字段,instance 字段将不会被覆盖。

public sealed class DataSingleton {
  private static instance;
  public static Instance {
    get {
      var result = instance;

      // early exit if singleton is already initialized
      if (result is not null) return result;

      var created = new DataSingleton();
      // thread-safe way how to assign "created" into "instance" only if "instance" refers to null. othervise no assignment will be made
      var original = Interlocked.CompareExchange(ref instance, null, created);

      // some other thread already initialized singleton
      if (original is not null) return original;

      // return newly created instance
      return result;
    }
  }
}

或者您可以使用lock来确保只创建一个实例。

public sealed class DataSingleton {
  private static instance;
  public static Instance {
    get {
      var result = instance;

      // early exit if singleton is already initialized
      if (result is not null) return result;

      lock(typeof(DataSingleton)) {
        result = instance;

        // double check, if instance was not initialized by another thread
        if (result is not null) return result;
    
        return instance = new DataSingleton();
      }
    }
  }
}

鉴于是网络服务器有问题,请在应用程序进入后台时停止它。当应用 returns 时再次启动它(或根据需要延迟启动)。

代码可能是这样的:

App.xaml.cs:

public static Webserver MyWebServer
{
    get
    {
        if (_server == null)
        {
            _server = new Webserver(...);
        }

        return _server;
    }
}
public static void StopWebServer()
{
    if (_server != null)
    {
        _server.Dispose();
        // So will be created again, on next reference to MyWebServer.
        _server = null;
    }
}
private static Webserver _server;

...

protected override void OnSleep()
{
    StopWebServer();
}

其他地方的用法:

... App.MyWebServer ...

如果你不想制作静态变量(尽管恕我直言,这对 App 来说是可以的,因为只有一个,而且它的生命周期是应用程序本身的生命周期),然后删除上面的“静态”,其他地方的用法变为:

... (Xamarin.Forms.Application.Current as App).MyWebServer ...