Это немного сложно объяснить. У меня есть поток, созданный в WindowsForm, поток не является приоритетом и выполняет свою работу, если никто не использовал ресурс исключительно с помощью Monitor.TryEnter.
Но при закрытии формы я использую событие FormClosing, чтобы остановить обработку, сохранить обработанные данные перед закрытием и т. Д. При попытке взять ресурс с другим Monitor.Enter .. создается блок.
Но забавно то, что следующим образом это работает !! Использование нового / вторичного потока или задачи для блокировки внутреннего потока и выполнения окончательного задания буферизации сохранения.
"Пример минимального, полного и проверяемого:"
namespace WindowsFormsApplication3
{
using System;
using System.Drawing;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
public partial class Form1 : Form
{
private readonly object _syncRoot = new object();
private volatile bool _working = false;
readonly Label lblInfo = new Label();
public Form1()
{
InitializeComponent();
SuspendLayout();
lblInfo.Text = "...";
lblInfo.Location = new Point(0, 0);
Controls.Add(lblInfo);
CheckBox chkBox = new CheckBox();
chkBox.Text = "Use Task inside FormClosing";
chkBox.AutoSize = true;
chkBox.Location = new Point(Width / 2 - chkBox.Width / 2, Height / 2 + chkBox.Height);
chkBox.Click += ChkBox_Click; ;
Controls.Add(chkBox);
ResumeLayout(false);
FormClosing += Form1_FormClosing;
}
private CancellationTokenSource _cancellationTokenS;
private bool _flUseThread = false;
private void Form1_Load(object sender, EventArgs e)
{
_cancellationTokenS = new CancellationTokenSource();
CancellationToken token = _cancellationTokenS.Token;
_working = true;
Task.Run(() =>
{
while (_working)
{
if (token.IsCancellationRequested)
break;
processData(DateTime.Now);
}
}, token);
}
private void ChkBox_Click(object sender, EventArgs e)
{
_flUseThread = ((CheckBox)sender).Checked;
}
private bool _alreadySafedClose = false;
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (_flUseThread)
{
if (_alreadySafedClose) return;
if (MessageBox.Show(this,
"Do you really want to cancel?", "Question:",
MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
e.Cancel = true; // Avoid close form.
Enabled = false; // Avoid interact with form.
Task.Factory.StartNew(() =>
{
bool lockWasTaken = false;
try
{
_working = false;
// Lock other thread.
Monitor.Enter(_syncRoot, ref lockWasTaken);
_cancellationTokenS.Cancel();
saveBufferResults();
MessageBox.Show("OK with Thread");
}
catch (Exception)
{
// ignored
}
finally
{
// Release lock other thread if already locked.
if (lockWasTaken)
Monitor.Exit(_syncRoot);
Invoke((MethodInvoker)delegate
{
_alreadySafedClose = true;
Close(); //Finally Close.
});
}
});
}
else
e.Cancel = true;
}
else
{
if (MessageBox.Show(this,
"Do you really want to cancel?", "Question:",
MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
bool lockWasTaken = false;
try
{
_working = false;
_cancellationTokenS.Cancel();
// Lock other thread avoid continue processing.
Monitor.Enter(_syncRoot, ref lockWasTaken); // <------ here it stays blocked!!
saveBufferResults();
MessageBox.Show("OK");
}
catch (Exception)
{
Console.WriteLine("<DEBUG> The data could not be processed.!");
}
finally
{
// Release lock other thread if already locked.
if (lockWasTaken)
Monitor.Exit(_syncRoot);
}
}
else
e.Cancel = true;
}
}
private void processData(DateTime data)
{
//flag to reduce locks..
if (!_working) return;
if (Monitor.TryEnter(_syncRoot))
{
try
{
//To escape quickly
if (_working)
innerProcessFrame(data);
}
catch (Exception)
{
Console.WriteLine("<DEBUG> The data could not be processed.!");
}
finally
{
Monitor.Exit(_syncRoot);
}
}
}
private ulong _dataCounter = 0;
private void innerProcessFrame(DateTime data)
{
Thread.Sleep(100);
Invoke((MethodInvoker)delegate
{
lblInfo.Text = data.Millisecond + " " + _dataCounter;
});
_dataCounter++;
}
private void saveBufferResults()
{
Thread.Sleep(1);
}
}
}
Я считаю, что FormClosing нарушает контекст синхронизации или блокирует поток работы и
поэтому в случае потока работы он никогда не достигает линии, где он освобождает ресурс
используя Monitor.Exit, в методе processData (...):
Мой вопрос: почему это происходит только при закрытии формы в событии FormClosing ?, потому что
если я помещаю кнопку в форму и помещаю тот же код блокировки, все останавливается правильно, то нет DeadLock, и почему он работает, создавая вторичный поток для выполнения окончательной работы при закрытии формы? и, наконец, если кто-то знает лучше или правильный способ ее решения.