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

时间:2012-05-03 16:23:57

标签: c# multithreading xna

我在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);
    }
}

这是加载和保存位图的函数。在form1.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;
        }
    }

3 个答案:

答案 0 :(得分:2)

此图像是否存储在像PictureBox这样的Control中?如果是这样,请确保您只在控件的线程上访问它。查看Control.Invoke()

答案 1 :(得分:1)

来自System.Threading.Timer的回调在ThreadPool主题上运行。在该回调中,您正在访问Form实例。你根本做不到这一点。好吧,你可以,但它无法正常工作。它将失败,不可预测,有时甚至是惊人的。

请改用System.Windows.Forms.Timer。每4秒钟打一次。在Tick事件中,从表单中获取您需要的任何数据,然后将其传递给另一个线程以进行进一步处理。

在下面的代码中,我通过在UI线程上调用Bitmap来获取DrawToBitmap对象。然后我将Bitmap关闭到Task,然后在后台转换为bool[]。 (可选)您可以从bool[]返回Task,然后在必要时调用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;
}

答案 2 :(得分:0)

在互联网上进行了一些搜索之后我发现了这一点,我需要另外一个函数来返回bool[,],这就是我提出的:

public bool[,] invokeBitmap(uint treshold) {
        if (this.InvokeRequired) {
            return (bool[,])this.Invoke((Func<bool[,]>)delegate {
                return getBoolBitmap(treshold);
            });
        } else {
            return getBoolBitmap(treshold);
        }
    }