ios - SqlCommand 对象的构造函数和 Dispose 方法在 Mono 中是线程安全的吗?
<p><p>以下是我在 iOS xamarin 应用程序中使用的一些数据访问代码的简化表示:</p>
<pre><code>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;}
//...
public void Dispose()
{
DataReader.Dispose();
SemaphoreLock.Release();
}
}
</code></pre>
<p>由于我们知道 Sqlite 不支持连接上的多个线程,因此我们将 SemaphoreSlim 放在适当的位置以确保连接的使用和任何命令的执行仅发生在单个线程中。 SemaphoreSlim 随后会在阅读器用尽并处置后释放。</p>
<p>但是,当我有多个线程调用 <code>GetAllFoos</code> 时,应用程序会崩溃并显示以下内容:</p>
<pre>2015-08-19 14:33:22.296 MyCoolIosApp critical: Stacktrace:
2015-08-19 14:33:22.296 MyCoolIosApp critical: at
2015-08-19 14:33:22.297 MyCoolIosApp 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 critical: at Mono.Data.Sqlite.SQLite3.Prepare (Mono.Data.Sqlite.SqliteConnection,string,Mono.Data.Sqlite.SqliteStatement,uint,string&) 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 critical: at Mono.Data.Sqlite.SqliteCommand.BuildNextCommand () 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 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 critical: at Mono.Data.Sqlite.SqliteDataReader.NextResult () 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 critical: at Mono.Data.Sqlite.SqliteDataReader..ctor (Mono.Data.Sqlite.SqliteCommand,System.Data.CommandBehavior) 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 critical: at Mono.Data.Sqlite.SqliteCommand.ExecuteReader (System.Data.CommandBehavior) 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 critical: at Mono.Data.Sqlite.SqliteCommand.ExecuteReader () 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
...
</pre>
<p>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 <code>GetAllFoos</code> 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:</p>
<pre><code>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;
}
</code></pre>
<p>据我所知,SqliteCommand 上的 Dispose 方法(可能还有构造函数)导致了并发问题。</p>
<p>SqliteCommand 对象的构造函数和 Dispose 方法在 Mono 中是线程安全的吗?或者我是否需要将一个或两个都考虑为用于锁定目的的关键部分?</p></p>
<br><hr><h1><strong>Best Answer-推荐答案</ strong></h1><br>
<p><p>使用 Mono 的好处在于它是开源的,所以让我们回答您的问题“SqliteCommand 对象的构造函数和 Dispose 方法在 Mono 中是线程安全的吗?”</p>
<p>如果我们在这里快速浏览一下:</p>
<p> <a href="https://github.com/mono/mono/blob/master/mcs/class/Mono.Data.Sqlite/Mono.Data.Sqlite_2.0/SQLiteCommand.cs#L108" rel="noreferrer noopener nofollow">https://github.com/mono/mono/blob/master/mcs/class/Mono.Data.Sqlite/Mono.Data.Sqlite_2.0/SQLiteCommand.cs#L108</a> </p>
<p>这里:</p>
<p> <a href="https://github.com/mono/mono/blob/master/mcs/class/Mono.Data.Sqlite/Mono.Data.Sqlite_2.0/SQLiteCommand.cs#L153" rel="noreferrer noopener nofollow">https://github.com/mono/mono/blob/master/mcs/class/Mono.Data.Sqlite/Mono.Data.Sqlite_2.0/SQLiteCommand.cs#L153</a> </p>
<p>...那么我们可以着重回答“不,Mono 的 SQLiteCommand 的构造函数和 Dispose 都不是线程安全的”。</p>
<p>就此而言,假设任何地方都没有线程安全是正确的,除非它通过文档明确说明。即便如此,有时它也是一个谎言。</p>
<p>(现在微软的东西也是开源的,所以我们可以在 Mono 之外用同样的方式回答这些问题。耶!)</p></p>
<p style="font-size: 20px;">关于ios - SqlCommand 对象的构造函数和 Dispose 方法在 Mono 中是线程安全的吗?,我们在Stack Overflow上找到一个类似的问题:
<a href="https://stackoverflow.com/questions/32104351/" rel="noreferrer noopener nofollow" style="color: red;">
https://stackoverflow.com/questions/32104351/
</a>
</p>
页:
[1]