环境: winform项目,使用EF6访问Sqlite,CodeFirst模式。所有组件都是直接在VS的Nuget管理器中下载的。
问题:我的一张Users表里有3个用户:张三、李四、王五,每个用户都拥有初始积分1000。我开启两个线程,线程内各自创建自己的dbcontext,然后各自去修改3个用户的积分。第一个线程是对每个用户积分都加100,第二个线程是对“张三”这一个玩家的积分减50。期望得到的结果应该是张三比其它玩家的积分少50,但实际执行时,常常出现,张三的积分是950,其它用户是1100,相差了150。这里应该是第二个线程后执行,且覆盖了第一个线程的写操作。怎么会出现这种情况呢?按说Sqlite作为数据库,不能保证这种基本的原子性吗?
代码:
访问Sqlite的DbContext类
public class SqliteContext : DbContext
{
public DbSet<User> Users { get; set; }
public SqliteContext() : base("SQLiteConnect")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
var init = new SqliteCreateDatabaseIfNotExists<SqliteContext>(modelBuilder);
Database.SetInitializer(init);
}
}
添加初始数据:
private void Form1_Load(object sender, EventArgs e)
{
//添加初始用户
List<User> userList = new List<User>();
userList.Add(new User() { Name = "张三", Score = 1000 });
userList.Add(new User() { Name = "李四", Score = 1000 });
userList.Add(new User() { Name = "王五", Score = 1000 });
using (var context = new SqliteContext())
{
foreach (var item in userList)
{
var user = context.Users.Where(s => s.Name == item.Name).FirstOrDefault();
if (user != null)
{
continue;
}
else
{
context.Users.Add(item);
}
}
context.SaveChanges();
}
}
开启线程去修改用户积分:
private void button1_Click(object sender, EventArgs e)
{
//执行加分操作
Task.Factory.StartNew(ModifyAllUserScore);
Task.Factory.StartNew(ModifySingleUserScore);
}
//为所有用户加100分
private async Task ModifyAllUserScore()
{
using (var context = new SqliteContext())
{
//先等待1秒
await Task.Delay(1000);
foreach (var user in context.Users)
{
user.Score += 100;
}
await context.SaveChangesAsync();
}
}
//仅为“张三”减少50分
private async Task ModifySingleUserScore()
{
using (var context = new SqliteContext())
{
//先等待1秒
await Task.Delay(1000);
var user = await context.Users.Where(s => s.Name == "张三").FirstOrDefaultAsync();
if (user != null)
{
user.Score -= 50;
}
await context.SaveChangesAsync();
}
}