我正在尝试构建一台ATM,我需要展示一个数据竞赛的例子。
为此,我需要两个线程在同一时间写入变量(我认为)。
当单击“Take out 10 $”按钮时,线程会写入此变量,但我无法在同一时间单击两个按钮,这意味着我无法真正显示数据竞争。这是按钮点击事件的代码。
private void button1_Click(object sender, EventArgs e)
{
if (Form1.currAcc.getBalance() < 10)
{
System.Windows.Forms.MessageBox.Show("Insuficient Funds");
this.Close();
}
else
{
Form1.currAcc.setBalance(Form1.currAcc.getBalance() - 10);
System.Windows.Forms.MessageBox.Show("Succesfully withdrawn 10$");
this.Close();
}
}
编辑:这最终给了我一个数据竞赛:
private void button1_Click(object sender, EventArgs e)
{
if (Form1.currAcc.getBalance() < 10)
{
System.Windows.Forms.MessageBox.Show("Insuficient Funds");
this.Close();
}
else
{
currentBalance = Form1.currAcc.getBalance();
Form5.timerStatus++;
if (Form5.timerStatus == 1)
{
Thread.Sleep(3000);
Form1.currAcc.setBalance(currentBalance - 10);
System.Windows.Forms.MessageBox.Show("Succesfully withdrawn 10$");
}
else{
Form1.currAcc.setBalance(currentBalance - 10);
System.Windows.Forms.MessageBox.Show("Succesfully withdrawn 10$");
Form5.timerStatus = 0;
}
this.Close();
}
}
答案 0 :(得分:2)
这很简单:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Program
{
static void Main()
{
for (int i = 0; i < 4; ++i)
Task.Run(() => withdraw(800m));
Thread.Sleep(1000);
// Balance should be 200 unless a race condition occured.
Console.WriteLine(balance);
}
static void withdraw(decimal amount)
{
if (balance >= amount)
balance -= amount;
}
static decimal balance = 1000.0m;
}
}
如果出现竞争条件,那么balance
将会结束。
当我运行时,就会发生这种情况。
如果按如下方式添加锁定,您将始终获得200
的正确结果:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Program
{
static void Main()
{
for (int i = 0; i < 4; ++i)
Task.Run(() => withdraw(800m));
Thread.Sleep(1000);
// Balance should be 200 unless a race condition occured.
Console.WriteLine(balance);
}
static void withdraw(decimal amount)
{
lock (locker)
{
if (balance >= amount)
balance -= amount;
}
}
static decimal balance = 1000.0m;
static readonly object locker = new object();
}
}
答案 1 :(得分:2)
&#34;代码&#34;答案都很好,但我认为有一个概念性的事情值得指出:你的多线程在完全相同的时间点做某事并不重要。
这些问题的实质是:因为缺少&#34;同步&#34;当这些线程进行更新时,无法预测它。因此,您最终会得到不同的结果,具体取决于首先进入的线程。
因此,让多个线程并行增加一个计数器就足够了 - 如果你在&#34;未同步的&#34;方式,最终结果(很可能)不匹配预期值。
含义:像
这样简单的东西fetch the current counter
do something else that takes a bit of time
write "counter+1" to counter
足够好了。而且这些写入发生并且#34;关闭&#34;这并不重要。及时;唯一需要的是读取和写入的顺序无法预测!
答案 2 :(得分:1)
演示竞争条件的最简单方法是使用Parallel库,因为它将同时启动多个线程。下面的代码很好地展示了它
Parallel.For(1, 11, r =>
{
Console.WriteLine($"Thread{r}: Balance: ${_balance}");
if (_balance < r)
{
Console.WriteLine($"Thread{r}: Insufficient funds ${_balance}");
return;
}
_balance -= r;
Console.WriteLine($"Thread{r}: Withdraw ${r}. New Balance is ${_balance}");
});
Console.WriteLine("Press any key to exit");
Console.Read();
它将生成这样的输出
线程1:余额:20美元 Thread1:提取1美元。新余额为19美元 线程6:余额:$ 19
线程2:余额:20美元 线程3:余额:20美元 线程4:余额:20美元 线程5:余额:$ 19
线程8:余额:4美元 线程8:资金不足4美元 线程9:余额:4美元 线程9:资金不足4美元 线程10:余额:4美元 线程10:资金不足4美元 Thread4:提取4美元。新余额为4美元 线程5:资金不足4美元 线程6:提取6美元。 New Balance是13美元 线程2:提取2美元。 New Balance是11美元 线程7:余额:11美元 线程7:资金不足4美元 线程3:提取3美元。 New Balance是8美元 按任意键退出
这清楚地表明线程最初看到的余额和它实际从中减去的余额是不一样的,例如,线程5在启动时看到20美元,但是当它试图提取5美元时,它获得的资金响应不足