C# wpf multithreading - c#

I want to generate 3 random number in threads then save it to list. Here's my code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;
using System.IO;
namespace Multithreading1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
List<int> myList = new List<int>();
int threadNumber = 0;
int currentRecNumber = -1;
public MainWindow()
{
InitializeComponent();
}
void ThreadHandler(int recNumber,int number)
{
Action action = null;
action = () =>
{
myList[recNumber] = number;
++currentRecNumber;
--threadNumber;
if (currentRecNumber < myList.Count)
{
++threadNumber;
Thread t = new Thread(() => GetRandomNumber(currentRecNumber));
t.Start();
}
else
if (threadNumber == 0) //finish
{
List<String> stringList = new List<String>();
for (int i = 0; i < myList.Count;i++)
{
stringList.Add(myList[i].ToString());
}
File.WriteAllLines("C:\\Users\\Public\\Documents\\MyList.txt", stringList);
System.Windows.MessageBox.Show("Finish");
}
};
this.Dispatcher.BeginInvoke(action);
}
void GetRandomNumber(int recNumber)
{
Random rnd = new Random();
int randomInt = rnd.Next(1, 13);
ThreadHandler(recNumber, randomInt);
}
private void button1_Click(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 20; i++)
{
myList.Add(-1);
}
for (int i = 0; i < 3; i++)
{
++currentRecNumber;
++threadNumber;
Thread t = new Thread(() => GetRandomNumber(currentRecNumber));
t.Start();
}
}
}
}
The problem are:
1. Sometimes it throw ArgumentOutOfRangeException at myList[recNumber] = number;
2. If it get past (1) the resulting file still contain -1s, eg:
-1
-1
8
6
11
-1
1
3
-1
3
3
8
8
8
8
10
10
10
10
12
Anyone know what is wrong ?
Thanks in advance.

Your Dispatcher.BeginInvoke will call each action on the thread the dispatcher is associated with, so you are effectively not actually running the actions on different threads. It might be better to do as much as you can in the ThreadHandler method, and only make UI changes inside the BeginInvoke action.
Also in your button1_Click, you increment currentRecNumber before you start each thread, so that will cause the first few threads to skip the first few items in the list.
You also have a major issue because you are accessing shared variables (currentRecNumber, threadNumber, and myList) from different threads, which can cause all manner of threading issues. You need to use some sort of synchronisation to ensure that each thread is reading and writing the correct values from these variables. You can use the InterlockedIncrement and InterlockedDecrement to mitigate some of these issues, but not all of them.
I'll also point out that creating threads is expensive, it's much better to schedule the work you want doing on thread pool threads , use a BackgroundWorker, or use one of the parallelism libraries, like the Task Parallel Library or PLINQ.
I would recommend having a read of this free ebook on threading by Joe Albahari.

Thanks Matt for the ebook. It's very easy to understand. I managed to fix my code with minor addition. The key of its problem is "Lambda expressions and captured variables", so I add couple local variables to it.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;
using System.IO;
namespace Multithreading1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
List<int> myList = new List<int>();
int threadNumber = 0;
int currentRecNumber = -1;
public MainWindow()
{
InitializeComponent();
}
void ThreadHandler(int recNumber,int number)
{
Action action = null;
action = () =>
{
myList[recNumber] = number;
++currentRecNumber;
--threadNumber;
int localCurrentRecNumber = currentRecNumber;
int localThreadNumber = threadNumber;
if (localCurrentRecNumber < myList.Count)
{
++threadNumber;
Thread t = new Thread(() => GetRandomNumber(localCurrentRecNumber));
t.Start();
}
else
if (localThreadNumber == 0) //finish
{
List<String> stringList = new List<String>();
for (int i = 0; i < myList.Count;i++)
{
stringList.Add(myList[i].ToString());
}
File.WriteAllLines("C:\\Users\\Public\\Documents\\MyList.txt", stringList);
System.Windows.MessageBox.Show("Finish");
}
};
this.Dispatcher.BeginInvoke(action);
}
void GetRandomNumber(int recNumber)
{
Random rnd = new Random();
int randomInt = rnd.Next(1, 13);
ThreadHandler(recNumber, randomInt);
}
private void button1_Click(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 20000; i++)
{
myList.Add(-1);
}
for (int i = 0; i < 3; i++)
{
++currentRecNumber;
++threadNumber;
int localCurrentNumber = currentRecNumber;
Thread t = new Thread(() => GetRandomNumber(localCurrentNumber));
t.Start();
}
}
}
}

Related

Array of random numbers in C# / .NET framework

I am trying to create a lottery program that would generate random numbers between 1 - 35. These numbers need to be unique as well, they can not be the same. With C#, running on the .NET framework.
I have been trying to get it to work, but I keep getting errors and I don't understand why. I have been googling and looking at videos - still I don't get what I am doing wrong.
This is my code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace help
{
public partial class Form1 : Form
{
// The array
TextBox[] TextBoxArray;
// the counter on how many times u pressed btn
private int counter = 1;
public Form1()
{
InitializeComponent();
this.StartPosition = FormStartPosition.CenterScreen;
// The text boxes name 1 - 7
//TextBoxArray = new TextBox[] { Nr1, Nr2, Nr3, Nr4, Nr5, Nr6, Nr7 };
}
private void button1_Click(object sender, EventArgs e)
{
NrOfPull.Text = counter.ToString();
counter++;
// Keep getting an error
// Error CS0443 Syntax error; value
// they are talking about the Parse(TextBoxArray[].Text);
int number = int.Parse(TextBoxArray[].Text);
// Loop through
Random rand = new Random();
for (int i = 0; i < number; i++)
{
// Generate a alotter by turn
int storedNumber;
int[] randomLottoRow = new int[7];
for (int row = 0; row < 7; row++)
{
do
{
storedNumber = rand.Next(1, 35);
}
while (randomLottoRow.Contains(storedNumber));
randomLottoRow[row] = storedNumber;
}}
/*
* This will only let me generate numbers.. but wanna use Parse...
Random generator = new Random();
for (int x = 0; x < 7; x++)
{
TextBoxArray[x].Text = generator.Next(1, 35).ToString();
Console.WriteLine(TextBoxArray[x].Text = generator.Next(1, 35).ToString());
}
*/
}
}
}
Would love to get feedback on what I am doing wrong Thank you so much :)
This is one way of generating random sequences of numbers:
using System.Linq;
public static class Ex
{
static readonly Random rng = new Random();
/// <summary>
/// Randoms the sequence.
/// </summary>
/// <param name="maxValue">The maximum number in drawing.</param>
/// <param name="count">The number of numbers drawn.</param>
public static int[] Lottery(int maxValue, int count)
{
return Enumerable.Range(1, maxValue+1)
.OrderBy((x)=>rng.NextDouble())
.Take(count).ToArray();
}
}
static class Program
{
static void Main(string[] args)
{
var seq = Ex.Lottery(35, 8);
// draw 8 numbers ranging between 1-35
Console.WriteLine(string.Join(",", seq));
// 14,24,1,3,25,5,31,30
}
}
Here you go:
private TextBox[] TextBoxArray;
private Random rand = new Random();
public Form1()
{
InitializeComponent();
TextBoxArray = new TextBox[] { Nr1, Nr2, Nr3, Nr4, Nr5, Nr6, Nr7 };
}
private void button1_Click(object sender, EventArgs e)
{
int[] picks = Enumerable.Range(1, 35).OrderBy(x => rand.NextDouble()).Take(7).ToArray();
for(int i=0; i<picks.Count(); i++)
{
TextBoxArray[i].Text = picks[i].ToString();
}
}
This uses the same approach as John Alexiou for generating the random lottery pick numbers, but then also shows you how to put those numbers into your TextBoxes.

C# garbage collecting reasons?

I have the following code as a WPF app.
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
namespace TestLeak {
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
Random RND = new Random();
int SIZE = 100000;
double fValue0 = 0;
double fValue1 = 0;
double fValue2 = 0;
JArray aValues0 = new JArray(new double[SIZE]);
JArray aValues1 = new JArray(new double[SIZE]);
JArray aValues2 = new JArray(new double[SIZE]);
JArray aValues = new JArray();
aValues.Add(aValues0);
aValues.Add(aValues1);
aValues.Add(aValues2);
DispatcherTimer oTimer = new DispatcherTimer(DispatcherPriority.Normal);
oTimer.Tick += new EventHandler((oSender, aArgs) => {
for (int i = 0; i < SIZE; i++) {
fValue0 += 1;
fValue1 += 1;
fValue2 += 1;
aValues0[i] = fValue0;
aValues1[i] = fValue1;
aValues2[i] = fValue2;
}
});
oTimer.Interval = TimeSpan.FromMilliseconds(1000);
oTimer.Start();
}
}
}
I have no idea why the GC needs to work that hard, what is it collecting? It's just basic assignment and overwriting (kind of an)array values. Could it be an issue with NewtonSoft Jarray objects?
The issue is that for my app, the interval is way smaller (aiming for ~16ms) but on each step the GC alone takes 40+ms to complete.
Thank you!
Garbage Collection at work
You are creating 300k objects every tick because JArray.operator[] is of type JToken, which is a reference class. You're assigning doubles to them for every item of every one of your three arrays, which implicitly creates JValue instances:
public static implicit operator JToken(double value)
{
return new JValue(value);
}
That's what the garbage collector is collecting.

Async Await Loop/Math problems

I'm making a little program to practice with WPF and Async/Await for multithreading, and what the program does is:
Find all the prime numbers between two numbers "a" and "b", and spit them out to a textbox called "Prime1".
Simultaneously in a different task, find all the prime numbers between "c" and "d", and spit them out to a textbox called "Prime2".
A button in the window will allow the user to click it, and it will keep track of how many times it has been clicked, whilst the other two tasks find prime numbers, to demonstrate asynchronous operations.
The code is as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WPF_Asynch_Project
{
public partial class MainWindow : Window
{
public int ClickAmount = 0;
public MainWindow()
{
InitializeComponent();
DelegationIsAwesome();
}
private void Test_Click(object sender, RoutedEventArgs e)
{
ClickAmount++;
MessageBox.Show("You clicked me " + ClickAmount.ToString() + " times!");
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
}
private async void DelegationIsAwesome()
{
Task enumtask = new Task(() => FindPrimes(100000, 100000000));
Task[] enumall = new Task[2];
enumall[0] = enumtask;
enumall[1] = new Task(() => FindPrimes2(1000, 10000));
enumall.ToList().ForEach(t => t.Start());
await Task.WhenAll(enumall).ConfigureAwait(false);
}
private void FindPrimes(long lower, long upper)
{
for (long i = lower; i < upper; i++)
{
long primeornot = 1;
for (long q = 2; q < i; q++)
{
if (i % q == 0)
{
primeornot = 0;
}
}
if (primeornot == 1)
{
System.Threading.Thread.Sleep(6);
Prime1.Dispatcher.BeginInvoke(
(Action)(()=>{ Prime1.Text += i.ToString() + ", "; }));
}
}
}
private void FindPrimes2(int lower, long upper)
{
for (int i = lower; i < upper; i++)
{
int primeornot = 1;
for (int q = 2; q < i; q++)
{
if (i % q == 0)
{
primeornot = 0;
}
}
if (primeornot == 1)
{
System.Threading.Thread.Sleep(5);
Prime2.Dispatcher.BeginInvoke(
(Action)(() => { Prime2.Text += i.ToString() + ", "; }));
}
}
}
}
}
However I get odd results. The following is a picture from the program:
Obviously the output from the prime-finding methods is incorrect. But why does it keep repeating those same numbers? It also sometimes spits out a number equal to UpperBound even though "i" should never equal or be greater than UpperBound.
What is happening to my output, and how do I fix it?
This has nothing to do with async/await, really.
You're calling BeginInvoke here:
Prime1.Dispatcher.BeginInvoke(
(Action)(()=>{ Prime1.Text += i.ToString() + ", "; }));
... and your lambda expression uses i, which means it will append the current value of i when the delegate executes. That's not necessarily the value of i when you call BeginInvoke.
If you want to capture the value (rather than the variable) you basically need to instantiate a new variable each time. You might as well do the conversion to a string:
string textToAppend = i + ", ";
// No need for braces here...
Prime1.Dispatcher.BeginInvoke((Action)(() => Prime1.Text += textToAppend));
Because you've declared the variable textToAppend inside the loop, each iteration will create a delegate capturing a separate variable.
You need to do this in both of your methods.

How do you pass a function to itself a variable number of times to form an "n level method call"?

Suppose we have the bare bones C# code for random number generation.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace theBlindPainterTester
{
public partial class Form1 : Form
{
Random randomNumber = new Random();
public Form1()
{
InitializeComponent();
}
private void button_Click(object sender, EventArgs e)
{
randomNumber.Next(1, 6);
}
}
}
We could easily call Next() on itself to set the upper bound.
randomNumber.Next(1, randomNumber.Next(1, 6));
This could be referred to as a two level method call, a "random number between 1 and (a random number between 1 and 5)." Similarly we could write a three level method call.
randomNumber.Next(1, randomNumber.Next(1, randomNumber.Next(1, 6)));
How can we do this for N levels, where N is a variable not known until run time? I've attempted to mold out the logic using a recursive method, but it seems you would need to call Next() on every iteration for that to work, which requires immediate knowledge of parameters. Perhaps I'm just not thinking about it the correct way.
r = 6;
for (int k = 0; k < N; k++)
r = randomNumber.Next(1, r);
Wouldn't this work?
private int Generate(int Counter)
{
if (Counter>0)
return randomnumber.Next(1, Generate(Counter-1));
else
return randomnumber.Next(1, 6);
}

Task doesn't change parameters

I am playing with Task functions and found a pretty strange problem that I run Task in for loop and pass the parameters to function (i) the loop count is 100.
As I expected the output would be like this.
1
2
3
4
5
But the output I get from this function is
100
100
100
I mean it won't change to new parameters. For more details I uploaded the whole project.
Download Sample Program that I made!
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
CheckForIllegalCrossThreadCalls = false;
}
private void button1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(() => button_tast());
}
void button_tast()
{
Task[] tk =new Task[100];
for (int i = 0; i < 100; i++)
{
tk[i] = Task.Factory.StartNew(() => taskThread(i));
}
Task.WaitAll(tk);
}
void taskThread(int i){
listBox1.Items.Add(i);
}
}
}
It's because you're closing over the loop variable. You can rewrite the loop as
for (int i = 0; i < 100; i++)
{
int taskNumber = i
tk[i] = Task.Factory.StartNew(() => taskThread(taskNumber));
}
and you'll be fine
The problem is that from the point of view of the Tasks, the variable i is global, and can be modified in a non-threadsafe manner. This is easily rectified though....
for (int i = 0; i < 100; i++)
{
int localCopy = i;
tk[i] = Task.Factory.StartNew(() => taskThread(localCopy));
}

Categories

Resources