以下是我在 iOS xamarin 应用程序中使用的一些数据访问代码的简化表示:
public async Task<List<Foo>> GetAllFoos()
{
var foos = new List<Foo>();
using(CommandWrapper command = GetCommandWrapper("SELECT Id, Name, Rank FROM Foos"))//this creates SqliteCommand object and puts it in wrapper object
{
using(ReaderWrapper reader = ExecuteReaderAsync(_Connection, command))
{
while(reader.Read())
{
var foo = ConstructFoo(reader);//construct foo with column values read from reader
foos.Add(foo);
}
}
}
return foos;
}
private SemaphoreSlim _commandLockSemaphore = new SemaphoreSlim(1, 1);//only 1 thread at once
private async ExecuteReaderAsync(Mono.Data.Sqlite.SqliteConnection connection, CommandWrapper command)
{
if(!(await _SemaphoreLock.WaitAsync(_TimeoutTimespan))) throw new Exception("timeout");
//lock now held
Mono.Data.Sqlite.SqliteCommand sqliteCommand = command.UnderlyingCommand;
try
{
sqliteCommand.Connection = connection;
IDataReader rawReader = await sqliteCommand.ExecuteReaderAsync();
}
catch(Exception)
{
//emergency lock release
_SemaphoreLock.Release();
throw;
}
return new ReaderWrapper(){DataReader = rawReader, SemaphoreLock = _SemaphoreLock};
}
internal class ReaderWrapper : IDisposable
{
internal IDataReader DataReader{get; set;}
internal SemaphoreSlim SemaphoreLock{get; set;}
//... [read methods]
public void Dispose()
{
DataReader.Dispose();
SemaphoreLock.Release();
}
}
因为我们知道 Sqlite 不支持连接上的多个线程,所以我们放置了 SemaphoreSlim 以确保连接的使用和任何命令的执行仅发生在单个线程中。 SemaphoreSlim 随后在读取器耗尽并处理后释放。
但是,当我有多个线程调用 GetAllFoos 时,应用会崩溃并显示以下内容:
2015-08-19 14:33:22.296 MyCoolIosApp[8421:5311923] critical: Stacktrace: 2015-08-19 14:33:22.296 MyCoolIosApp[8421:5311923] critical: at 2015-08-19 14:33:22.297 MyCoolIosApp[8421:5311923] critical: at (wrapper managed-to-native) Mono.Data.Sqlite.UnsafeNativeMethods.sqlite3_prepare (intptr,intptr,int,intptr&,intptr&) 2015-08-19 14:33:22.297 MyCoolIosApp[8421:5311923] critical: at Mono.Data.Sqlite.SQLite3.Prepare (Mono.Data.Sqlite.SqliteConnection,string,Mono.Data.Sqlite.SqliteStatement,uint,string&) [0x00044] in //Library/Frameworks/Xamarin.iOS.framework/Versions/8.10.4.46/src/mono/mcs/class/Mono.Data.Sqlite/Mono.Data.Sqlite_2.0/SQLite3.cs:268 2015-08-19 14:33:22.298 MyCoolIosApp[8421:5311923] critical: at Mono.Data.Sqlite.SqliteCommand.BuildNextCommand () [0x00019] in //Library/Frameworks/Xamarin.iOS.framework/Versions/8.10.4.46/src/mono/mcs/class/Mono.Data.Sqlite/Mono.Data.Sqlite_2.0/SQLiteCommand.cs:230 2015-08-19 14:33:22.298 MyCoolIosApp[8421:5311923] critical: at Mono.Data.Sqlite.SqliteCommand.GetStatement (int) [0x0000b ] in //Library/Frameworks/Xamarin.iOS.framework/Versions/8.10.4.46/src/mono/mcs/class/Mono.Data.Sqlite/Mono.Data.Sqlite_2.0/SQLiteCommand.cs:264 2015-08-19 14:33:22.299 MyCoolIosApp[8421:5311923] critical: at Mono.Data.Sqlite.SqliteDataReader.NextResult () [0x000cc] in //Library/Frameworks/Xamarin.iOS.framework/Versions/8.10.4.46/src/mono/mcs/class/Mono.Data.Sqlite/Mono.Data.Sqlite_2.0/SQLiteDataReader.cs:914 2015-08-19 14:33:22.299 MyCoolIosApp[8421:5311923] critical: at Mono.Data.Sqlite.SqliteDataReader..ctor (Mono.Data.Sqlite.SqliteCommand,System.Data.CommandBehavior) [0x00051] in //Library/Frameworks/Xamarin.iOS.framework/Versions/8.10.4.46/src/mono/mcs/class/Mono.Data.Sqlite/Mono.Data.Sqlite_2.0/SQLiteDataReader.cs:89 2015-08-19 14:33:22.300 MyCoolIosApp[8421:5311923] critical: at Mono.Data.Sqlite.SqliteCommand.ExecuteReader (System.Data.CommandBehavior) [0x00006] in //Library/Frameworks/Xamarin.iOS.framework/Versions/8.10.4.46/src/mono/mcs/class/Mono.Data.Sqlite/Mono.Data.Sqlite_2.0/SQLiteCommand.cs:539 2015-08-19 14:33:22.300 MyCoolIosApp[8421:5311923] critical: at Mono.Data.Sqlite.SqliteCommand.ExecuteReader () [0x00000] in //Library/Frameworks/Xamarin.iOS.framework/Versions/8.10.4.46/src/mono/mcs/class/Mono.Data.Sqlite/Mono.Data.Sqlite_2.0/SQLiteCommand.cs:551 ...
According to various discussions, that error is caused by too many threads on Sqlite on iOS devices. I can corroborate this since it crashes 100% of the time if I have a pair of threads attempt to endlessly call GetAllFoos in a while loop; and also when I rearrange the locks (e.g. add a simple lock statement around construction and disposal of the command), it fixes the issue:
public async Task<List<Foo>> GetAllFoos()
{
var foos = new List<Foo>();
lock(_someStaticObject)//why is this needed
{
using(CommandWrapper command = GetCommandWrapper("SELECT Id, Name, Rank FROM Foos"))//this creates SqliteCommand object and puts it in wrapper object
{
using(ReaderWrapper reader = ExecuteReaderAsync(_Connection, command))
{
while(reader.Read())
{
var foo = ConstructFoo(reader);//construct foo with column values read from reader
foos.Add(foo);
}
}
}
}
return foos;
}
据我所知,SqliteCommand 上的 Dispose 方法(可能还有构造函数)导致了并发问题。
SqliteCommand 对象的构造函数和 Dispose 方法在 Mono 中是线程安全的吗?或者我是否需要将其中一个或两个都视为用于锁定目的的关键部分?
最佳答案
使用 Mono 的好处在于它是开源的,所以让我们来回答您的问题“SqliteCommand 对象的构造函数和 Dispose 方法在 Mono 中是线程安全的吗?”
如果我们快速浏览一下:
这里:
...那么我们可以着重回答“不,Mono 的 SQLiteCommand 的构造函数和 Dispose 都不是线程安全的”。
就此而言,假设任何地方都不是线程安全的是正确的,除非它通过文档明确说明。即便如此,它有时也是谎言。
(现在微软的东西也是开源的,所以我们可以在 Mono 之外以同样的方式回答这些问题。耶!)
关于ios - SqlCommand 对象的构造函数和 Dispose 方法在 Mono 中是线程安全的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32104351/
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev
我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss
好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信
我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A
我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re
如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象
我在Rails工作并有以下类(class):classPlayer当我运行时bundleexecrailsconsole然后尝试:a=Player.new("me",5.0,"UCLA")我回来了:=>#我不知道为什么Player对象不会在这里初始化。关于可能导致此问题的操作/解释的任何建议?谢谢,马里奥格 最佳答案 havenoideawhythePlayerobjectwouldn'tbeinitializedhere它没有初始化很简单,因为你还没有初始化它!您已经覆盖了ActiveRecord::Base初始化方法,但您没有调
我有一个服务模型/表及其注册表。在表单中,我几乎拥有服务的所有字段,但我想在验证服务对象之前自动设置其中一些值。示例:--服务Controller#创建Action:defcreate@service=Service.new@service_form=ServiceFormObject.new(@service)@service_form.validate(params[:service_form_object])and@service_form.saverespond_with(@service_form,location:admin_services_path)end在验证@ser
我正在尝试用ruby中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了