![](/img/trans.png)
[英]C# .NET WinForms. Visual studio won't recognise namespaces in subfolders
[英]c# .net 4.8 winforms. Lock object can be locked several times simultaneously in simple single thread app
重復的問題。 在這里回答...
鎖定 aqcuired 並且進一步嘗試鎖定不會阻塞:C# 鎖定是否可重入?
基本上(從上面的鏈接復制):
.NET 中的鎖是可重入的。 只有來自其他線程的獲取被阻塞。 當同一個線程多次鎖定同一個 object 時,它只是增加一個計數器,並在釋放時減少它。 當計數器達到零時,實際上釋放鎖以供其他線程訪問。
c# .net 4.8 winforms。 一個按鈕,一個多行文本框。
簡單的應用程序 - 單擊按鈕,它等待 2 秒(doevents 2 秒),它會寫出。
我按了 6 次按鈕,大約相隔一秒。 我知道 doevents 允許拾取更多點擊事件。 我可以看到,在處理完成之前的單擊之前,鎖沒有等待,並且事件以相反的順序完成。
Q1 - 為什么鎖不上鎖?
Q2 - 為什么點擊事件以相反的順序完成?
Q3 - 這里實際發生了什么?
using System.Windows.Forms;
namespace WindowsFormsApplication_testlock
{
public partial class Form1 : Form
{
object _objLock;
int _intIteration;
System.Collections.Generic.Dictionary<int, DateTime> _lstIterationsTimes;
public Form1()
{
InitializeComponent();
_objLock = new object();
_intIteration = 0;
_lstIterationsTimes = new Dictionary<int, DateTime>();
}
private void button1_Click(object sender, EventArgs e)
{
_intIteration++;
_lstIterationsTimes.Add(_intIteration, DateTime.Now);
Process(_intIteration);
}
private void Process(int intIteration)
{
textBox1.Text += "About to lock - " + intIteration.ToString() + Environment.NewLine;
lock (_objLock)
{
textBox1.Text += "Succeeded to lock - " + intIteration.ToString() + Environment.NewLine;
while ( (DateTime.Now - _lstIterationsTimes[intIteration]).TotalSeconds < 2)
{
Application.DoEvents();
}
textBox1.Text += "About to unlock - " + intIteration.ToString() + Environment.NewLine ;
}
}
}
}
Output:
即將鎖定 - 1
成功鎖定 - 1
即將鎖定 - 2
成功鎖定 - 2
即將鎖定 - 3
成功鎖定 - 3
即將鎖定 - 4
成功鎖定 - 4
即將鎖定 - 5
成功鎖定 - 5
即將鎖定 - 6
成功鎖定 - 6
即將解鎖 - 6
即將解鎖 - 5
即將解鎖 - 4
即將解鎖 - 3
即將解鎖 - 2
即將解鎖 - 1
Q1 - 為什么鎖不上鎖?
當您按下 button1 時,對button1_Click
的調用總是在同一個線程上,即 GUI 線程上。 在 C# 中,一個線程可能多次進入同一個鎖。
Q2 - 為什么點擊事件以相反的順序完成?
Q3 - 這里實際發生了什么?
見下文。
正在發生的事情以及原因可以總結在下圖中(為令人震驚的質量道歉 - 我在 PowerPoint 中做了這個) - 為了簡單起見,我將調用Process
方法的次數限制為 3:
發生的事情如下 - 所有都在 GUI 線程上:
button1_Click
被觸發,它將_intIteration
增加到1
然后調用Process(1)
。Process(1)
:
while
循環中,允許應用程序通過重復調用DoEvents()
來等待用戶的輸入。while
循環期間的某個時刻,再次單擊 button1。 在button1_Click
返回之前, DoEvents()
調用不會返回。button1_Click
被觸發,它將_intIteration
增加到2
然后調用Process(2)
。Process(2)
內部(仍在 GUI 線程上):
while
循環中,允許應用程序通過重復調用DoEvents()
來等待用戶的輸入。while
循環期間的某個時刻,再次單擊 button1。 在button1_Click
返回之前, DoEvents()
調用不會返回。button1_Click
被觸發,它將_intIteration
增加到3
然后調用Process(3)
。Process(3)
內部(仍在 GUI 線程上):
while
循環中,允許應用程序通過重復調用DoEvents()
來等待用戶的輸入。Process(3)
在打印“About to unlock - 3”后返回button1_Click
的第三級作為Process(3)
返回。DoEvents()
(從Process(2)
調用)返回,因此執行返回到Process(2)
。Process(2)
在打印“About to unlock - 2”后返回button1_Click
的第二級作為Process(2)
返回。DoEvents()
(從Process(1)
調用)返回,因此執行返回到Process(1)
。Process(1)
在打印“About to unlock - 1”后返回button1_Click
的第一級返回為Process(1)
返回。 請注意,一旦停止單擊 button1 並且退出while
循環, DoEvents()
方法中有多個堆棧幀。 這些方法必須以后進先出的順序退出,這就是Process(3)
退出然后Process(2)
退出然后Process(1)
退出的原因,導致
About to unlock - 3
About to unlock - 2
About to unlock - 1
你不確定。
由於這一切都發生在一個線程上,因此查看以下代碼可能更容易,該代碼向您展示了正在發生的事情:
private static object _objLock = new object();
public static void Main(params string[] arguments)
{
Recurse(1);
}
public static void Recurse(int iteration)
{
if ( iteration > 3 )
return;
Console.WriteLine($"About to lock - {iteration}");
lock ( _objLock )
{
Console.WriteLine($"Entered lock - {iteration}");
Recurse(iteration + 1);
Console.WriteLine($"Releasing lock - {iteration}");
}
}
將其寫入控制台:
About to lock - 1
Entered lock - 1
About to lock - 2
Entered lock - 2
About to lock - 3
Entered lock - 3
Releasing lock - 3
Releasing lock - 2
Releasing lock - 1
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.