Я не могу поверить в это, я всегда думал, что ниже будет безопасно для параллелизма.
Я записываю строку в одну транзакцию и могу прочитать грязное значение из другой транзакции / команды / соединения! Почему это возможно (не мой главный вопрос), разве это не желательно и вызывает больше проблем!?!
В любом случае, я ожидал, что, как только я напишу в строку, больше ничего не сможет прочитать в строку, пока транзакция не будет завершена. И, по крайней мере, если строка еще может быть прочитана, будет прочитано чистое (оригинальное) значение. (но, возможно, это также вызовет проблемы, если транзакция не использует вновь переданные данные из другой транзакции при запуске)
Я бы хотел сосчитать до == 11. Я думал, что это будет безопасно во всех вариантах sql. Что я могу сделать для 1) Не читать грязное значение, но очистить 2) Блокировать ли эту строку до завершения транзакции?
static MySqlConnection MakeConn()
{
string connStr = "server=192.168.126.128;user=root;database=TestDB;port=3306;password=a;";
MySqlConnection conn = new MySqlConnection(connStr);
conn.Open();
return conn;
}
static Semaphore sem1 = new Semaphore(1, 1);
static Semaphore sem2 = new Semaphore(1, 1);
static void Main2()
{
Console.WriteLine("Starting Test");
//
sem1.WaitOne(); Console.WriteLine("1W");
sem2.WaitOne(); Console.WriteLine("2W");
Thread oThread = new Thread(new ThreadStart(fn2));
oThread.Start();
var conn = MakeConn();
var cmd = new MySqlCommand(@"
CREATE TABLE IF NOT EXISTS Persons
(
P_Id int NOT NULL,
name varchar(255),
count int,
PRIMARY KEY (P_Id)
)", conn);
cmd.ExecuteNonQuery();
cmd.CommandText = "delete from Persons; insert into Persons(name, count) VALUES('E', '4');";
cmd.ExecuteNonQuery();
cmd.CommandText = "select count from Persons;";
var count = (int)cmd.ExecuteScalar();
Console.WriteLine("Finish inserting. v={0}", count);
sem2.Release(); Console.WriteLine("2R");
sem1.WaitOne(); Console.WriteLine("1W");
Console.WriteLine("Starting transaction");
using (var tns = conn.BeginTransaction())
{
cmd.CommandText = "update Persons set count=count+1";
cmd.ExecuteNonQuery();
cmd.CommandText = "select count from Persons;";
count = (int)cmd.ExecuteScalar();
Console.WriteLine("count is {0}", count);
sem2.Release(); Console.WriteLine("2R");
sem1.WaitOne(); Console.WriteLine("1W");
count += 5; //10
cmd.CommandText = "update Persons set count=" + count.ToString();
cmd.ExecuteNonQuery();
cmd.CommandText = "select count from Persons;";
count = (int)cmd.ExecuteScalar();
Console.WriteLine("count is {0}", count);
tns.Commit();
}
Console.WriteLine("finished transaction 1");
sem2.Release(); Console.WriteLine("2R");
sem1.WaitOne(); Console.WriteLine("1W");
cmd.CommandText = "select count from Persons;";
count = (int)cmd.ExecuteScalar();
Console.WriteLine("count is {0}", count);
sem2.Release(); Console.WriteLine("2R");
//sem1.WaitOne(); Console.WriteLine("1W");
}
static void fn2()
{
int count;
Console.WriteLine("Starting thread 2");
sem2.WaitOne(); Console.WriteLine("1W");
var conn = MakeConn();
var cmd = new MySqlCommand("", conn);
sem1.Release(); Console.WriteLine("1R");
sem2.WaitOne(); Console.WriteLine("2W");
using (var tns = conn.BeginTransaction())
{
cmd.CommandText = "update Persons set count=count+1";
cmd.ExecuteNonQuery();
cmd.CommandText = "select count from Persons;";
count = (int)cmd.ExecuteScalar();
Console.WriteLine("count is {0}", count);
sem1.Release(); Console.WriteLine("1R");
sem2.WaitOne(); Console.WriteLine("2W");
tns.Commit();
}
Console.WriteLine("finished transaction 2");
sem1.Release(); Console.WriteLine("1R");
sem2.WaitOne(); Console.WriteLine("2W");
cmd.CommandText = "select count from Persons;";
count = (int)cmd.ExecuteScalar();
Console.WriteLine("count is {0}", count); //should be 11. 4 + 1x2(one each thread) += 5 from first thread == 11
sem1.Release(); Console.WriteLine("1R");
}
Консоль
Starting Test
1W
2W
Starting thread 2
Finish inserting. v=4
2R
1W
1R
1W
Starting transaction
count is 5
2R
2W
count is 6
1R
1W
count is 10
finished transaction 1
2R
2W
finished transaction 2
1R
1W
count is 10
2R
2W
count is 10
1R