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.
Related
I'm in trouble on a College project.The project needs to be done using c# as a programming language and made in Windows Form like.
The program executes. I know it has flaws but at least i want to know how to get of this error: http://postimg.org/image/gwuzmyc73/ .
For the problem i need to fold a vector using the Divide et Impera.
I need to insert a number from the keyboard n, the generated vector would be like a=(1,2,3,4,5,6,7) and the final elements are 1,3,5,7.
The problem sounds like:
A vector of n elements.We define its folding by overlaping the 2 halfs,if n is odd.The 2 halfs are folded again until de subvector reaches 1 element.Utilize Divide et Impera.
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 Plierea_Vectorilor
{
public partial class Form1 : Form
{
public int n;
public int i;
public int[] efinal = new int[50];
public string m = new string(new char[50]);
public int Ls, Ld;
public char[] aux = new char[50];
public Form1()
{
InitializeComponent();
}
public void Pliaza(int p,int q)
{
if (p == q)
{
efinal[p] = 1;
}
else
{
if ((q - p + 1) % 2 != 0)
{
Ls = (p + q) / 2 - 1;
}
else
{
Ls = (p + q) / 2;
}
Ld = (p + q) / 2 + 1;
}
Pliaza(p, Ls);
Pliaza(Ld, q);
/*
string ss = Ls.ToString();
string sd = Ld.ToString();
for (i = p; i <= Ls; i++)
{
aux[0] = 'S';
string.Concat(aux, ss);
string.Concat(aux, " ");
string m = aux.ToString();
string.Concat(aux, m[i]);
}
for ( i = Ld; i <= q; i++)
{
aux[0] = 'D';
string.Concat(aux,Ld);
string.Concat(aux, " ");
string m = aux.ToString();
string.Concat(aux, m[i]);
}
*/
}
private void button1_Click(object sender, EventArgs e)
{
Pliaza(1, n);
for (i = 1; i <= n; i++)
{
if (efinal[i]!=0)
{
label2.Text = Convert.ToString(i);
}
}
}
private void label2_Click(object sender, EventArgs e)
{
}
}
}
You're ALWAYS executing the Pliaza function FROM Pliaza. Aren't you missing a finish condition?
The recursive loop must be finished sometime in someway, else you will get that stack overflow.
A StackOverflowException usually means you have uncontrolled recursion going on. Reviewing your code, I see that Pliaza calls itself twice, using different variables ... but there is no path for Pliaza to exit without calling itself. You need some path to let the recursion bottom out. Very likely this would be to add a return; statement after the efinal[p] = 1; line.
I was trying to solve this problem projecteuler,problem125
this is my solution in python(just for understanding the logic)
lim = 10**8
total=0
found= set([])
for start in xrange(1,int(lim**0.5)):
s=start**2
for i in xrange(start+1,int(lim**0.5)):
s += i**2
if s>lim:
break
if str(s) == str(s)[::-1]:
found.add(s)
print sum(found)
the same code I wrote in C# is as follows
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
public static bool isPalindrome(string s)
{
string temp = "";
for (int i=s.Length-1;i>=0;i-=1){temp+=s[i];}
return (temp == s);
}
static void Main(string[] args)
{
int lim = Convert.ToInt32(Math.Pow(10,8));
var found = new HashSet<int>();
for (int start = 1; start < Math.Sqrt(lim); start += 1)
{
int s = start *start;
for (int i = start + 1; start < Math.Sqrt(lim); i += 1)
{
s += i * i;
if (s > lim) { break; }
if (isPalindrome(s.ToString()))
{ found.Add(s); }
}
}
Console.WriteLine(found.Sum());
}
}
}
the code debugs fine until it gives an exception at Console.WriteLine(found.Sum()); (line31). Why can't I find Sum() of the set found
The sum is: 2,906,969,179.
That is 759,485,532 greater than int.MaxValue;
Change int to long in var found = new HashSet<long>(); To handle the value.
You can also use uint however instead of long, however I would recommend using long.
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));
}
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();
}
}
}
}
Can anyone tell me what the correct Plinq code is for this? I'm adding up the square root of the absolute value of the sine of each element fo a double array, but the Plinq is giving me the wrong result.
Output from this program is:
Linq aggregate = 75.8310477905274 (correct)
Plinq aggregate = 38.0263653589291 (about half what it should be)
I must be doing something wrong, but I can't work out what...
(I'm running this with Visual Studio 2008 on a Core 2 Duo Windows 7 x64 PC.)
Here's the code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
namespace ConsoleApplication1
{
class Program
{
static void Main()
{
double[] array = new double[100];
for (int i = 0; i < array.Length; ++i)
{
array[i] = i;
}
double sum1 = array.Aggregate((total, current) => total + Math.Sqrt(Math.Abs(Math.Sin(current))));
Console.WriteLine("Linq aggregate = " + sum1);
IParallelEnumerable<double> parray = array.AsParallel<double>();
double sum2 = parray.Aggregate((total, current) => total + Math.Sqrt(Math.Abs(Math.Sin(current))));
Console.WriteLine("Plinq aggregate = " + sum2);
}
}
}
Aggregate works slightly differently in PLINQ.
From MSDN Blogs:
Rather than expecting a value to
initialize the accumulator to, the
user gives us a factory function that
generates the value:
public static double Average(this IEnumerable<int> source)
{
return source.AsParallel().Aggregate(
() => new double[2],
(acc, elem) => { acc[0] += elem; acc[1]++; return acc; },
(acc1, acc2) => { acc1[0] += acc2[0]; acc1[1] += acc2[1]; return acc1; },
acc => acc[0] / acc[1]);
}
Now, PLINQ can initialize an
independent accumulator for each
thread. Now that each thread gets its
own accumulator, both the folding
function and the accumulator combining
function are free to mutate the
accumulators. PLINQ guarantees that
accumulators will not be accessed
concurrently from multiple threads .
So, in your case, you would also need to pass an accumulator function which sums the outputs of the paralleled aggregates (hence why you're seeing a result that is roughly half of what it should be).
Thank you MSDN Blogs. It now seems to be working correctly. I changed my code as follows:
using System;
using System.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main()
{
Test();
}
static void Test()
{
double[] array = new double[100];
for (int i = 0; i < array.Length; ++i)
{
array[i] = i;
}
double sum1 = array.Aggregate((total, current) => total + Math.Sqrt(Math.Abs(Math.Sin(current))));
Console.WriteLine("Linq aggregate = " + sum1);
IParallelEnumerable<double> parray = array.AsParallel();
double sum2 = parray.Aggregate
(
0.0,
(total1, current1) => total1 + Math.Sqrt(Math.Abs(Math.Sin(current1))),
(total2, current2) => total2 + current2,
acc => acc
);
Console.WriteLine("Plinq aggregate = " + sum2);
}
}
}