C# 多個執行續存取 同一個 SQLITE 檔案(SQLite_multi_thread_test_CS)
C# 多個執行續存取 同一個 SQLITE 檔案(SQLite_multi_thread_test_CS)
資料來源 : chatgpt [ https://chatgpt.com/share/690c367d-3d78-8009-a07a-c930ad35f83d ]
GITHUB : https://github.com/jash-git/SQLite_multi_thread_test_CS
開發工具 : Visual Studio 2022 C#8.0
相依套件 :
– Microsoft.Data.Sqlite.Core(9.0.10)
– SQLitePCLRaw.bundle_e_sqlite3(3.0.2)
or
– System.Data.SQLite.Core(1.0.119)
◆code(Microsoft.Data.Sqlite.Core) ~ 微軟官方(EF Core 同步)
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Data.Sqlite;
using SQLitePCL;
//---
//資料來源: https://chatgpt.com/share/690c367d-3d78-8009-a07a-c930ad35f83d
//---
class Program
{
static void Pause()
{
Console.Write("Press any key to continue...");
Console.ReadKey(true);
}
static void Main()
{
const string dbFile = "multi_thread_test.db";
Batteries.Init();// 初始化 SQLitePCL 底層提供者
// 若檔案不存在就初始化資料庫
if (!File.Exists(dbFile))
{
InitializeDatabase(dbFile);
}
// 啟用 WAL 模式(支援多執行緒多讀單寫)
using (var conn = new SqliteConnection($"Data Source={dbFile}"))
{
conn.Open();
using var cmd = conn.CreateCommand();
cmd.CommandText = "PRAGMA journal_mode=WAL;";
var mode = cmd.ExecuteScalar();
Console.WriteLine($"WAL 模式啟用結果: {mode}");
}
Console.WriteLine("開始平行寫入...");
var connStr = new SqliteConnectionStringBuilder
{
DataSource = dbFile,
Cache = SqliteCacheMode.Shared,
Mode = SqliteOpenMode.ReadWrite
}.ToString();
// 使用 20 個執行緒平行寫入資料
Parallel.For(0, 20, i =>
{
for (int j = 0; j < 5; j++)
{
WriteWithRetry(connStr, i, j);
}
});
Console.WriteLine("寫入完成。");
Pause();
}
static void InitializeDatabase(string dbFile)
{
using var conn = new SqliteConnection($"Data Source={dbFile}");
conn.Open();
using var cmd = conn.CreateCommand();
cmd.CommandText = @"
CREATE TABLE log_table (
id INTEGER PRIMARY KEY AUTOINCREMENT,
thread_id INTEGER,
msg TEXT,
created_at TEXT
);
";
cmd.ExecuteNonQuery();
conn.Close();
Console.WriteLine("資料庫已初始化。");
}
static void WriteWithRetry(string connStr, int threadId, int seq)
{
const int maxRetry = 5;
for (int retry = 0; retry < maxRetry; retry++)
{
try
{
using var conn = new SqliteConnection(connStr);
conn.Open();
using var cmd = conn.CreateCommand();
cmd.CommandText = "INSERT INTO log_table (thread_id, msg, created_at) VALUES (@t, @m, datetime('now'))";
cmd.Parameters.AddWithValue("@t", threadId);
cmd.Parameters.AddWithValue("@m", $"Thread {threadId} - record {seq}");
cmd.ExecuteNonQuery();
return; // 成功就跳出
}
catch (SqliteException ex) when (ex.SqliteErrorCode == 5) // database is locked
{
int delay = 100 * (retry + 1);
Console.WriteLine($"[Thread {threadId}] 資料庫被鎖定,重試第 {retry + 1} 次 (延遲 {delay}ms)");
Thread.Sleep(delay);
}
catch (Exception ex)
{
Console.WriteLine($"[Thread {threadId}] 錯誤: {ex.Message}");
return;
}
}
}
}
◆code(System.Data.SQLite.Core) ~ SQLite 官方(原生 .NET 封裝)
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Data.SQLite;
class Program
{
static void Pause()
{
Console.Write("Press any key to continue...");
Console.ReadKey(true);
}
static void Main()
{
const string dbFile = "multi_thread_sqlite_test.db";
// 若檔案不存在就初始化資料庫
if (!File.Exists(dbFile))
{
InitializeDatabase(dbFile);
}
// 啟用 WAL 模式(多執行緒寫入推薦)
using (var conn = new SQLiteConnection($"Data Source={dbFile};Version=3;"))
{
conn.Open();
using var cmd = new SQLiteCommand("PRAGMA journal_mode=WAL;", conn);
var result = cmd.ExecuteScalar();
Console.WriteLine($"WAL 模式啟用結果: {result}");
}
Console.WriteLine("開始平行寫入...");
string connStr = $"Data Source={dbFile};Version=3;Cache=Shared;Journal Mode=WAL;";
// 使用 20 個執行緒同時寫入資料
Parallel.For(0, 20, i =>
{
for (int j = 0; j < 5; j++)
{
WriteWithRetry(connStr, i, j);
}
});
Console.WriteLine("寫入完成。");
Pause();
}
static void InitializeDatabase(string dbFile)
{
SQLiteConnection.CreateFile(dbFile);
using var conn = new SQLiteConnection($"Data Source={dbFile};Version=3;");
conn.Open();
using var cmd = new SQLiteCommand(@"
CREATE TABLE log_table (
id INTEGER PRIMARY KEY AUTOINCREMENT,
thread_id INTEGER,
msg TEXT,
created_at TEXT
);
", conn);
cmd.ExecuteNonQuery();
Console.WriteLine("資料庫已初始化。");
}
static void WriteWithRetry(string connStr, int threadId, int seq)
{
const int maxRetry = 5;
for (int retry = 0; retry < maxRetry; retry++)
{
try
{
using var conn = new SQLiteConnection(connStr);
conn.Open();
using var cmd = new SQLiteCommand(
"INSERT INTO log_table (thread_id, msg, created_at) VALUES (@t, @m, datetime('now'))",
conn);
cmd.Parameters.AddWithValue("@t", threadId);
cmd.Parameters.AddWithValue("@m", $"Thread {threadId} - record {seq}");
cmd.ExecuteNonQuery();
return; // 成功就離開
}
catch (SQLiteException ex)
{
if (ex.ErrorCode == (int)SQLiteErrorCode.Busy) // 資料庫被鎖定
{
int delay = 100 * (retry + 1);
Console.WriteLine($"[Thread {threadId}] 資料庫被鎖定,重試第 {retry + 1} 次 (延遲 {delay}ms)");
Thread.Sleep(delay);
continue;
}
Console.WriteLine($"[Thread {threadId}] SQLite 錯誤: {ex.Message}");
return;
}
catch (Exception ex)
{
Console.WriteLine($"[Thread {threadId}] 一般錯誤: {ex.Message}");
return;
}
}
}
}
One thought on “C# 多個執行續存取 同一個 SQLITE 檔案(SQLite_multi_thread_test_CS)”
GITHUB 有包含copilot的範例
🧪 範例說明
使用 System.Data.SQLite
啟用 WAL 模式與 Shared Cache
多執行緒同時執行讀寫操作
using System;
using System.Data.SQLite;
using System.Threading;
using System.Threading.Tasks;
class Program
{
private const string ConnectionString = "Data Source=sample.db;Version=3;Cache=Shared;Journal Mode=WAL;Synchronous=Normal;";
private static readonly SemaphoreSlim WriteLock = new SemaphoreSlim(1, 1);
static async Task Main(string[] args)
{
// 初始化資料庫
InitializeDatabase();
// 建立多個執行緒進行讀寫
var tasks = new Task[10];
for (int i = 0; i
{
await WriteDataAsync($"Thread-{threadId}", threadId);
ReadData();
});
}
await Task.WhenAll(tasks);
Console.WriteLine("所有執行緒完成。");
}
static void InitializeDatabase()
{
using var conn = new SQLiteConnection(ConnectionString);
conn.Open();
using var cmd = new SQLiteCommand("CREATE TABLE IF NOT EXISTS Logs (Id INTEGER PRIMARY KEY AUTOINCREMENT, ThreadName TEXT, Value INTEGER)", conn);
cmd.ExecuteNonQuery();
}
static async Task WriteDataAsync(string threadName, int value)
{
await WriteLock.WaitAsync();
try
{
using var conn = new SQLiteConnection(ConnectionString);
conn.Open();
using var cmd = new SQLiteCommand("INSERT INTO Logs (ThreadName, Value) VALUES (@name, @val)", conn);
cmd.Parameters.AddWithValue("@name", threadName);
cmd.Parameters.AddWithValue("@val", value);
cmd.ExecuteNonQuery();
Console.WriteLine($"寫入:{threadName} - {value}");
}
finally
{
WriteLock.Release();
}
}
static void ReadData()
{
using var conn = new SQLiteConnection(ConnectionString);
conn.Open();
using var cmd = new SQLiteCommand("SELECT * FROM Logs", conn);
using var reader = cmd.ExecuteReader();
while (reader.Read())
{
Console.WriteLine($"讀取:{reader["ThreadName"]} - {reader["Value"]}");
}
}
}
📌 注意事項
Journal Mode=WAL 是關鍵,能讓讀寫並行。
SemaphoreSlim 保護寫入區段,避免鎖定衝突。
Cache=Shared 允許多連線共享快取,提升效能。