如何使用计时器访问线程中的信息

本文关键字:信息 线程 访问 何使用 计时器 | 更新日期: 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);
        }
    }