Reputation: 46
I have been breaking my head over this for a long time now and I can't seem to find how to solve this. Simply put I need to present Parallel programming to my class and i am making a demo that clearly shows the diffrence between single threaded and multi threaded.
First the program takes a screenshot and loads that in this class into a bitmap
.
Then it loads the winform with a picturebox
called screenshot
. Next it does Form1_Load
and loads the screenshot into the picturebox
.
Then Form1_Shown
Runs the for loop which just scrambles the pixels around based on a randomizer and updates the image from the left to the right.
example: http://gyazo.com/ab04583bb33de59d08407886da1c4870
This all works. I now want to make it so that the screenshot is being updated from the left to the middle by the first thread, and from the middle to the right with the second thread.
But when i put it in 2 separate threads it says "An unhandled exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll"
The error implies I am making illegal cross thread calls. Particulary on screenshot
and probably also on mybitmap
.
Visual studio helps me by linking me to "How to: Make Thread-Safe Calls to Windows Forms Controls"
But i am not getting any wiser out of this information which is probably because i am not that fluent with the C# terminology yet.
How should i approach/fix this ?
This is the class where everything happens (except taking the screenshot):
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));
}
}
}
}
}
Upvotes: 0
Views: 183
Reputation: 1151
You will need to Invoke the GUI thread. I prefer the following solution. Using a Form member ('screenshot' in this case) check to see if InvokeRequired = true. If so then Invoke a delegate using that members BeginInvoke() function as the following example demonstrates:
private void ScreenshotRefresh()
{
if(screenshot.InvokeRequired)
{
screenshot.BeginInvoke(new MethodInvoker(this.ScreenshotRefresh));
}
else
{
screenshot.Refresh();
}
}
Upvotes: 2