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 ...
我在 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 ...