如何使用计时器访问线程中的信息
本文关键字:信息 线程 访问 何使用 计时器 | 更新日期: 2023-09-27 18:32:59
我在C#
中创建了一个表单,其中有一个图像正在转换为布尔数组。我正在尝试生成一个带有计时器的线程,其中图像每秒转换 4 次。
当我调试它时,它可以工作,我可以从计时器跟踪滴答声。但是当表单运行时,应用程序会退出而不会给出错误。
这是初始化脚本:
form = new LoadForm();
form.Show();
form.BringToFront();
timer = new System.Threading.Timer(new TimerCallback(camTick), null, 0, 250);
这是有效的勾号:
private void camTick(Object myObject)
{
if (form.isRunning)
{
bool[,] ar = form.getBoolBitmap(100);
}
}
这是加载和保存位图的函数。在形式1中.cs
public bool[,] getBoolBitmap(uint threshold) {
unsafe {
Bitmap b = getBitmap();
BitmapData originalData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
bool[,] ar = new bool[b.Width, b.Height];
for (int y = 0; y < b.Height; y++) {
byte* Row = (byte*)originalData.Scan0 + (y * originalData.Stride);
for (int x = 0; x < b.Width; x++) {
if ((byte)Row[x * 3] < threshold) {
ar[x, y] = false;
} else {
ar[x, y] = true;
}
}
}
b.Dispose();
return ar;
}
}
public Bitmap getBitmap() {
if (!panelVideoPreview.IsDisposed) {
Bitmap b = new Bitmap(panelVideoPreview.Width, panelVideoPreview.Height, PixelFormat.Format24bppRgb);
using (Graphics g = Graphics.FromImage(b)) {
Rectangle rectanglePanelVideoPreview = panelVideoPreview.Bounds;
Point sourcePoints = panelVideoPreview.PointToScreen(new Point(panelVideoPreview.ClientRectangle.X, panelVideoPreview.ClientRectangle.Y));
g.CopyFromScreen(sourcePoints, Point.Empty, rectanglePanelVideoPreview.Size);
}
return b;
} else {
Bitmap b = new Bitmap(panelVideoPreview.Width, panelVideoPreview.Height);
return b;
}
}
此图像是否存储在像 PictureBox 这样的控件中?如果是这样,请确保仅在控件的线程上访问它。查看 Control.Invoke()。
来自System.Threading.Timer
的回调在ThreadPool
线程上运行。在该回调中,您正在访问Form
实例。你根本做不到这一点。好吧,你可以,但它将无法正常工作。它将失败得不可预测,有时甚至是壮观的。
请改用System.Windows.Forms.Timer
。让它每 4 秒滴答一次。在Tick
事件中,从表单中获取所需的任何数据,然后将其传递给另一个线程进行进一步处理。
在下面的代码中,我通过在 UI 线程上调用 DrawToBitmap
来获取 Bitmap
对象。然后,我将Bitmap
传递给Task
,在那里它可以在后台转换为bool[]
。或者,您可以从Task
返回该bool[]
,然后在必要时调用ContinueWith
将其传输回 UI 线程(听起来您可能不需要这样做)。
private void YourWindowsFormsTimer_Tick(object sender, EventArgs args)
{
// Get the bitmap object while still on the UI thread.
var bm = new Bitmap(panelVideoPreview.ClientSize.Width, panelVideoPreview.ClientSize.Height, PixelFormat.Format24bppRgb);
panelVideoPreview.DrawToBitmap(bm, panelVideoPreview.ClientRectangle);
Task.Factory
.StartNew(() =>
{
// It is not safe to access the UI here.
bool[,] ar = ConvertBitmapToByteArray(bm);
DoSomethingWithTheArrayHereIfNecessary(ar);
// Optionally return the byte array if you need to transfer it to the UI thread.
return ar;
})
.ContinueWith((task) =>
{
// It is safe to access the UI here.
// Get the returned byte array.
bool[,] ar = task.Result;
UpdateSomeControlIfNecessaryHere(ar);
}, TaskScheduler.FromCurrentSynchronizationContext());
}
ConvertBitmapToByteArray
现在看起来像这样。
public unsafe bool[,] ConvertBitmapToBoolArray(Bitmap b, uint threshold)
{
BitmapData originalData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
bool[,] ar = new bool[b.Width, b.Height];
for (int y = 0; y < b.Height; y++)
{
byte* Row = (byte*)originalData.Scan0 + (y * originalData.Stride);
for (int x = 0; x < b.Width; x++)
{
if ((byte)Row[x * 3] < treshold)
{
ar[x, y] = false;
} else
{
ar[x, y] = true;
}
}
}
b.Dispose();
return ar;
}
在互联网上搜索了一番后,我发现了这一点,我需要制作另一个函数来返回bool[,]
,这就是我想出的:
public bool[,] invokeBitmap(uint treshold) {
if (this.InvokeRequired) {
return (bool[,])this.Invoke((Func<bool[,]>)delegate {
return getBoolBitmap(treshold);
});
} else {
return getBoolBitmap(treshold);
}
}