制作一个更新的 winform 多线程
Making an updating winform multi threaded
我已经为此苦思良久,似乎找不到解决方法。简而言之,我需要向我的 class 展示并行编程,我正在制作一个演示,清楚地显示单线程和多线程之间的差异。
首先,程序截屏并将此 class 中的截图加载到 bitmap
中。
然后它使用名为 screenshot
的 picturebox
加载 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();
}
}
我已经为此苦思良久,似乎找不到解决方法。简而言之,我需要向我的 class 展示并行编程,我正在制作一个演示,清楚地显示单线程和多线程之间的差异。
首先,程序截屏并将此 class 中的截图加载到 bitmap
中。
然后它使用名为 screenshot
的 picturebox
加载 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();
}
}