制作一个更新的 winform 多线程

Making an updating winform multi threaded

我已经为此苦思良久,似乎找不到解决方法。简而言之,我需要向我的 class 展示并行编程,我正在制作一个演示,清楚地显示单线程和多线程之间的差异。

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

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

一切正常。 我现在想让第一个线程从左向中间更新屏幕截图,第二个线程从中间向右更新屏幕截图。

但是当我把它放在 2 个单独的线程中时它说 "An unhandled exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll"

该错误暗示我正在进行非法的跨线程调用。特别是 screenshot,可能还有 mybitmap。 Visual studio 通过将我链接到 "How to: Make Thread-Safe Calls to Windows Forms Controls"

来帮助我

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

我应该如何 approach/fix 这个?

这是 class 一切发生的地方(截屏除外):

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

您将需要调用 GUI 线程。 我更喜欢以下解决方案。 使用表单成员('screenshot' 在这种情况下 )检查 InvokeRequired = true。如果是这样,则使用该成员 BeginInvoke() 函数调用委托,如下例所示:

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