【问题标题】:Making an updating winform multi threaded使更新的winform多线程
【发布时间】:2015-02-05 22:54:40
【问题描述】:

我已经为此烦恼了很长时间,但我似乎无法找到解决此问题的方法。简而言之,我需要向我的班级展示并行编程,并且我正在制作一个演示,清楚地展示单线程和多线程之间的差异。

首先,程序截取屏幕截图并将其加载到此类中的bitmap 中。 然后它使用名为screenshotpicturebox 加载winform。接下来它执行Form1_Load 并将屏幕截图加载到picturebox。 然后Form1_Shown 运行 for 循环,它只是根据随机化器对周围的像素进行打乱,并从左到右更新图像。

示例:http://gyazo.com/ab04583bb33de59d08407886da1c4870

这一切都有效。 我现在想让屏幕截图由第一个线程从左到中更新,第二个线程从中到右更新。

但是当我将它放在 2 个单独的线程中时,它会显示“System.Windows.Forms.dll 中发生了类型为 'System.InvalidOperationException' 的未处理异常”

该错误意味着我正在进行非法的跨线程调用。特别是在screenshot 上,也可能在mybitmap 上。 Visual Studio 通过将我链接到 "How to: Make Thread-Safe Calls to Windows Forms Controls" 来帮助我

但是我没有从这些信息中得到任何更明智的信息,这可能是因为我对 C# 术语还不是很流利。

我应该如何处理/解决这个问题?

这是发生所有事情的类(除了截屏):

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ScreenOutput
{
    public partial class Form1 : Form
    {
    PictureBox screenshot;
    Bitmap myBitmap = new Bitmap(@".\screenshot.jpg");

    public Form1()
    {
        InitializeComponent();
    }

    private int Xcount;
    private int Ycount;
    private int maxXValue = Screen.PrimaryScreen.Bounds.Width - 1;

    private int maxXValueT1 = 960;
    private int maxXValueT2 = 1919;

    private int maxYValue = Screen.PrimaryScreen.Bounds.Height - 1;
    private int maxYValueT1 = 539;
    private int maxYValueT2 = 1079;

    private void Form1_Load(object sender, EventArgs e)
    {
        screenshot.Image = myBitmap;
    }

    private void Form1_Shown(object sender, EventArgs e)
    {
        Thread startThread1 = new Thread(new ThreadStart(thread1));
        Thread startThread2 = new Thread(new ThreadStart(thread2));
        startThread1.Start();
        startThread2.Start();
        Thread.Sleep(10000); //waiting for completion
        startThread1.Abort();
        startThread2.Abort();

        //this is how it would work without multithreading
        /*Random random = new Random();
        for (Xcount = 0; Xcount < maxXValue; Xcount++)
        {

            screenshot.Refresh();
            for (Ycount = 0; Ycount < maxYValue; Ycount++)
            {
                int calculatedX = Xcount + random.Next(0, maxXValue);
                if (calculatedX > maxXValue) calculatedX = maxXValue;
                myBitmap.SetPixel(Xcount, Ycount, myBitmap.GetPixel(calculatedX, Ycount));

            }
        }
        Thread.Sleep(2000);*/
        Application.Exit();
    }

    public void thread1()
    {
        Random random = new Random();
        for (Xcount = 0; Xcount < maxXValueT1; Xcount++)
        {

            screenshot.Refresh();
            for (Ycount = 0; Ycount < maxYValueT1; Ycount++)
            {
                int calculatedX = Xcount + random.Next(0, maxXValueT1);
                if (calculatedX > maxXValue) calculatedX = maxXValueT1;
                myBitmap.SetPixel(Xcount, Ycount, myBitmap.GetPixel(calculatedX, Ycount));
            }
        }
    }

    public void thread2()
    {
        Random random = new Random();
        for (Xcount = 0; Xcount < maxXValueT2; Xcount++)
        {

            screenshot.Refresh();
            for (Ycount = 0; Ycount < maxYValueT2; Ycount++)
            {
                int calculatedX = Xcount + random.Next(0, maxXValueT2);
                if (calculatedX > maxXValueT2) calculatedX = maxXValueT2;
                myBitmap.SetPixel(Xcount, Ycount, myBitmap.GetPixel(calculatedX, Ycount));
            }
        }           
    }
}
}

【问题讨论】:

  • 曾经考虑使用Timer
  • @MethodMan 不确定如何使用计时器解决问题?我对此还是很陌生。
  • 不太可能 SO 可以为您提供比链接文章更多的帮助...关于 SO 也有许多类似的问题,但显然他们都在谈论相同的修复 - 如果您可能会得到对您更有用的答案您将问题范围缩小到您从文章中不理解的特定概念。

标签: c# .net multithreading winforms


【解决方案1】:

您需要调用 GUI 线程。 我更喜欢以下解决方案。 使用表单成员(在本例中为“屏幕截图”)检查 InvokeRequired 是否 = true。如果是这样,则使用该成员 BeginInvoke() 函数调用委托,如下例所示:

private void ScreenshotRefresh()
 {         
     if(screenshot.InvokeRequired)
     {
         screenshot.BeginInvoke(new MethodInvoker(this.ScreenshotRefresh));    
     }
     else
     {
         screenshot.Refresh();
     }
 }

【讨论】:

  • 那么我只会在线程中调用 Screenshot refresh 方法吗? (如果我会使用第二个例子?)
  • 正确,在您的线程函数中将 screenshot.Refresh(); 更改为 ScreenshotRefresh(); 此外,您可能对 multi-在 myBitmap 上同时运行的线程操作。可能需要调用一些线程安全函数。
  • 好吧,我可能还没拿到这部分。但是现在VS说:错误1'System.Windows.Forms.PictureBox'不包含'BeginInVoke'的定义并且没有扩展方法'BeginInVoke'接受'System.Windows.Forms.PictureBox'类型的第一个参数可以找到(您是否缺少 using 指令或程序集引用?)
  • 啊,是的,我对 mybitmap 的看法也是如此。仍然不确定这是如何工作的。是否与您建议使用screenshot 解决它的方式相同?
  • 哇哈哈,我也遇到过......很多:)。无论如何,你是对的,现在我在mybitmap 上遇到了问题,如果我在两个线程之一中禁用mybitmap.setpixel,它会部分工作。我猜他不想同时设置像素
猜你喜欢
  • 1970-01-01
  • 2012-07-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-06
  • 2012-09-08
  • 2013-02-09
  • 2013-10-13
相关资源
最近更新 更多