How can I keep updating my ListBox synchronously - c#

I am calculating prime numbers bw two numbers using following code
private static IEnumerable<int> GetPrimes(int from, int to)
{
for (int i = from; i <= to; i++)
{
bool isPrime = true;
int limit = (int)Math.Sqrt(i);
for (int j = 2; j <= limit; j++)
if (i % j == 0)
{
isPrime = false;
break;
}
if (isPrime)
{
yield return i;
}
}
}
And I want to update my list box without blocking my UI thread, with all the prime numbers using above code. The approch which I am using as following but this is not working out.
public MainWindow()
{
InitializeComponent();
_worker = new BackgroundWorker();
_worker.DoWork += _worker_DoWork;
this.DataContext = this;
}
private void _worker_DoWork(object sender, DoWorkEventArgs e)
{
PrimeNumbers = new ObservableCollection<int>();
foreach (var item in GetPrimes(1, 10000000))
{
Dispatcher.BeginInvoke(new Action<int>(Test), item);
}
}
private void Test(int obj)
{
PrimeNumbers.Add(obj);
}
public ObservableCollection<int> PrimeNumbers
{
get
{
return primeNumbers;
}
set
{
primeNumbers = value;
OnPropertyChanged("PrimeNumbers");
}
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
_worker.RunWorkerAsync();
}
but this approach freezes my UI. I want to have result continuously coming from the GetPrimes method and keep adding to my listboz

You are just posting too much. This code works as expected:
public partial class MainWindow
{
public ObservableCollection<int> PrimeNumbers { get; set; }
public MainWindow()
{
InitializeComponent();
PrimeNumbers = new ObservableCollection<int>();
}
private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
{
PrintPrimes(PrimeNumbers.Add, 1, 10000000, SynchronizationContext.Current);
}
private static void PrintPrimes(Action<int> action, int from, int to,
SynchronizationContext syncContext)
{
Task.Run(() =>
{
for (var i = from; i <= to; i++)
{
var isPrime = true;
var limit = (int) Math.Sqrt(i);
for (var j = 2; j <= limit; j++)
{
if (i%j == 0)
{
isPrime = false;
break;
}
}
if (isPrime)
{
syncContext.Post(state => action((int)state), i);
Thread.Sleep(1);
}
}
});
}
}
Consider avoiding old BackgroundWorker class. Also, instead of using a synchronization mechanism of your platform try to switch to platform independent SynchronizationContext.
Instead of sleeping a thread you can post your results in bunches:
public partial class MainWindow
{
public ObservableCollection<int> PrimeNumbers { get; set; }
public MainWindow()
{
InitializeComponent();
PrimeNumbers = new ObservableCollection<int>();
}
private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
{
PrintPrimes(items => items.ForEach(PrimeNumbers.Add),
1, 10000000, SynchronizationContext.Current);
}
private static void PrintPrimes(Action<List<int>> action, int from, int to,
SynchronizationContext syncContext)
{
Task.Run(() =>
{
var primesBuffer = new List<int>();
for (var i = from; i <= to; i++)
{
var isPrime = true;
var limit = (int) Math.Sqrt(i);
for (var j = 2; j <= limit; j++)
{
if (i%j == 0)
{
isPrime = false;
break;
}
}
if (isPrime)
{
primesBuffer.Add(i);
if (primesBuffer.Count >= 1000)
{
syncContext.Post(state => action((List<int>) state),
primesBuffer.ToList());
primesBuffer.Clear();
}
}
}
});
}
}
You can use Thread instead of Task.Run if you're stuck with older versions of a framework.

That code looks ok, you just forgot to start your background worker by calling BackgroundWorker.RunWorkerAsync.
PrimeNumbers = new ObservableCollection<int>();
This line needs to be outside your worker (or being invoked in the UI thread as well).

It seems like my UI was getting upadated very frequently so I have used a delay of 1 second on my background worker thread. It helped me to achieve my functionality
private void _worker_DoWork(object sender, DoWorkEventArgs e)
{
foreach (var item in GetPrimes(1, 1000000))
{
Thread.Sleep(1000);
Dispatcher.BeginInvoke(new Action<int>(Test), item);
}
}

Related

Fill list box from another thread

I'm searching for this for several hours now and was not able to find proper solution. I'm c# beginner.
I have a winforms app with a ListBox and a class that does some work and should run forever on separate thread. I want to push MyDataStruct to ListBox each time its created in WorkerClass.Work.
Later on, several WorkerClass instances should run simultaneously and I will have combobox to pick which instance data to feed to ListBox . Is it better to have WorkerClas return only single MyDataStruct and keep their queue in Form1 class or have a queue in each WorkerClass and exchange the entire queue with Form1 every time it changes?
is my void QueueToLb good way to add queue data to ListBox ?
thank you for your support.
public partial class Form1 : Form
{
Queue<MyDataStruct> qList;
MyDataStruct myDataStruct;
private void RunTask()
{
//how do I make MyLongTask to update either qList or myDataStuct
Task.Run(() =>
{
MyLongTask(0, 1000);
});
}
private void MyLongTask(int low, int high)
{
WorkerClass wc = new WorkerClass();
wc.Work(low,high);
}
private void QueueToLb()
{
//is this good way to update listbox from queue?
List<MyDataStruct> lstMds = qList.Reverse<MyDataStruct>().ToList<MyDataStruct>();
List<string> lstStr = new List<string>();
foreach (MyDataStruct m in lstMds)
{
lstStr.Add(m.ToString());
}
listBox1.DataSource = lstStr;
}
}
public class WorkerClass
{
Queue<MyDataStruct> qList; //not sure if its better to keep the queue here or in Form1
public WorkerClass()
{
qList = new Queue<MyDataStruct>();
}
public void Work(int low, int high) //does some work forever
{
while (true)
{
if (qList.Count > 11) qList.Dequeue();
MyDataStruct mds = new MyDataStruct();
Random random = new Random();
mds.dt = DateTime.Now;
mds.num = random.Next(low, high);
qList.Enqueue(mds);
Thread.Sleep(1000);
}
}
}
public class MyDataStruct
{
public DateTime dt;
public int num;
public override string ToString()
{
StringBuilder s = new StringBuilder();
s.Append(num.ToString());
s.Append(" - ");
s.Append(dt.ToShortDateString());
return s.ToString();
}
}
OK I think I figured how to use BackgroundWorker on this, I'll be happy if someone could verify it is correct
public partial class Form1 : Form
{
Queue<MyDataStruct> qList;
BackgroundWorker bw = new BackgroundWorker();
public Form1()
{
InitializeComponent();
bw.WorkerReportsProgress = true;
bw.DoWork += new DoWorkEventHandler(Bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
}
private void Form1_Load(object sender, EventArgs e)
{
qList = new Queue<MyDataStruct>(12);
}
private void button1_Click(object sender, EventArgs e)
{
bw.RunWorkerAsync();
}
private void MyLongTask(int low = 0, int high = 1000)
{
WorkerClass wc = new WorkerClass(bw);
wc.Work(low,high);
}
private void BindToLbWithQueue()
{
MyDataStruct mds = new MyDataStruct();
Random random = new Random();
mds.dt = DateTime.Now;
mds.num = random.Next(0, 1000);
qList.Enqueue(mds);
QueueToLb();
}
private void QueueToLb()
{
//is this good way to update listbox from queue?
List<MyDataStruct> lstMds = qList.Reverse<MyDataStruct>().ToList<MyDataStruct>();
List<string> lstStr = new List<string>();
foreach (MyDataStruct m in lstMds)
{
lstStr.Add(m.ToString());
}
listBox1.DataSource = lstStr;
}
#region worker
private void Bw_DoWork(object sender, DoWorkEventArgs e)
{
MyLongTask();
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
qList = (Queue<MyDataStruct>)e.UserState;
QueueToLb();
}
#endregion
}
public class WorkerClass
{
Queue<MyDataStruct> qList; //not sure if its better to keep the queue here or in Form1
BackgroundWorker bw = null;
public WorkerClass(BackgroundWorker bw)
{
this.bw = bw;
qList = new Queue<MyDataStruct>();
}
public void Work(int low, int high) //does some work forever
{
while (true)
{
if (qList.Count > 11) qList.Dequeue();
MyDataStruct mds = new MyDataStruct();
Random random = new Random();
mds.dt = DateTime.Now;
mds.num = random.Next(low, high);
qList.Enqueue(mds);
bw.ReportProgress(0, qList);
Thread.Sleep(1000);
}
}
}
public class MyDataStruct
{
public DateTime dt;
public int num;
public override string ToString()
{
StringBuilder s = new StringBuilder();
s.Append(num.ToString());
s.Append(" - ");
s.Append(dt.ToShortDateString());
return s.ToString();
}
}

C# How to make a repeating method in BackgroundWorker (without blocking GUI), which changes imageIndex in TreeView

I have simple WindowsForm application, which contains TreeView. When initializing - TreeView is building from XML with default imageIndexes. My TreeView consists of servers names. In Tag element I put Dictionary which consists of a host and IP. After the initialization I call method, wich change the imageIndex if server is timedOut. I need call this method in BackgroundWorker that non block main thread (GUI). I plan to run this method every minute. Below is this method:
private void checkServersTree()
{
TreeNodeCollection rootNodes = treeViewSrv.Nodes;
TreeNodeCollection childNodes;
PingServers ps = new PingServers();
for (int i = 0; i < rootNodes.Count; i++)
{
childNodes = treeViewSrv.Nodes[i].Nodes;
treeViewSrv.Nodes[i].Text += string.Format(" ({0})", childNodes.Count);
int downServers = 0;
foreach (TreeNode tNode in childNodes)
{
if (tNode.Tag != null)
{
Dictionary<string, string> dicParams = tNode.Tag as Dictionary<string, string>;
if (!ps.getServerStatus(dicParams["host"], dicParams["ip"]))
{
tNode.ImageIndex = 1; //red
rootNodes[i].ImageIndex = 2; //yellow
downServers++;
}
}
}
if(downServers == childNodes.Count)
rootNodes[i].ImageIndex = 4; //fatal red
}
}
Thanks in advance!
Thanks for answer!
Before I write here I read about Invoke and BeginInvoke. But I can't solve this problem - run asynchronous thread that changing TreeView ImageIndexes. I went for your link and wrote code (below), but GUI stay blocked. What am I doing wrong?
namespace CCCServers
{
public partial class CCCServers : Form
{
public delegate void checkSrvDelegate();
public checkSrvDelegate myDelegate;
private Thread myThread;
public CCCServers()
{
InitializeComponent();
TreeFromXML tfXML = new TreeFromXML(treeViewSrv, "../../Servers.xml");
tfXML.initTreeNodesFromXML();
treeViewSrv.ExpandAll();
//checkServersTree();
myDelegate = new checkSrvDelegate(checkServersTree);
}
private void checkServersTree()
{
TreeNodeCollection rootNodes = treeViewSrv.Nodes;
TreeNodeCollection childNodes;
PingServers ps = new PingServers();
for (int i = 0; i < rootNodes.Count; i++)
{
childNodes = treeViewSrv.Nodes[i].Nodes;
treeViewSrv.Nodes[i].Text += string.Format(" ({0})", childNodes.Count);
int downServers = 0;
foreach (TreeNode tNode in childNodes)
{
if (tNode.Tag != null)
{
Dictionary<string, string> dicParams = tNode.Tag as Dictionary<string, string>;
if (!ps.getServerStatus(dicParams["host"], dicParams["ip"]))
{
tNode.ImageIndex = 1; //red
rootNodes[i].ImageIndex = 2; //yellow
downServers++;
}
}
}
if(downServers == childNodes.Count)
rootNodes[i].ImageIndex = 4; //fatal red
}
}
private void btnRDP_Click(object sender, EventArgs e)
{
myThread = new Thread(new ThreadStart(threadFunction));
myThread.Start();
}
private void threadFunction()
{
MyThreadClass myThreadClassObject = new MyThreadClass(this);
myThreadClassObject.Run();
}
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
{
MessageBox.Show(e.Node.Text, "Info", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private void treeViewSrv_AfterSelect(object sender, TreeViewEventArgs e)
{
e.Node.SelectedImageIndex = e.Node.ImageIndex; //Что бы иконка не менялась при выборе узла
}
}
//***************************************************************************
public class MyThreadClass
{
CCCServers cccSrv;
public MyThreadClass(CCCServers myForm)
{
cccSrv = myForm;
}
public void Run()
{
cccSrv.Invoke(cccSrv.myDelegate);
}
}
}
You need to use form's Invoke method to execute user interface methods on the thread that created window handles. E.g.
Invoke((Action) checkServersTree);
I solve my problem. After insert on my code in special places this Debug.WriteLine(String.Format("<<<<<<<< This is {0} thread", Thread.CurrentThread.ManagedThreadId)); I realized that the Control.Invoke is called in the main thread, and therefore all who take the time to realize not to the Control.Invoke. See the code below:
private delegate void setServersCountDelegate(int idx, int cnt);
private delegate void setChldNodeImgIndexDelegate(TreeNode tNode, int imgIdx);
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
testBuildTree();
}
private void testBuildTree()
{
Thread.CurrentThread.Name = "CheckServers";
Debug.WriteLine(String.Format("|||||||||| This is {0} thread", Thread.CurrentThread.ManagedThreadId));
changeImgIndex();
}
private void changeImgIndex()
{
Debug.WriteLine(String.Format("--------- This is {0} thread", Thread.CurrentThread.ManagedThreadId));
TreeNodeCollection rootNodes = treeViewSrv.Nodes;
TreeNodeCollection childNodes;
for (int i = 0; i < rootNodes.Count; i++)
{
childNodes = treeViewSrv.Nodes[i].Nodes;
//treeViewSrv.Nodes[i].Text += string.Format(" ({0})", childNodes.Count);
treeViewSrv.Invoke(new setServersCountDelegate(setServersCount), new object[] { i, childNodes.Count });
int downServers = 0;
foreach (TreeNode tNode in childNodes)
{
if (tNode.Tag != null)
{
Dictionary<string, string> dicParams = tNode.Tag as Dictionary<string, string>;
if (!getServerStatus(dicParams["host"], dicParams["ip"]))
{
Debug.WriteLine(String.Format("<<<<<<<< This is {0} thread", Thread.CurrentThread.ManagedThreadId));
//tNode.ImageIndex = 1; //red
treeViewSrv.Invoke(new setChldNodeImgIndexDelegate(setChldNodeImgIndex), new object[] { tNode, 1 }); //red
//rootNodes[i].ImageIndex = 2; //yellow
treeViewSrv.Invoke(new setChldNodeImgIndexDelegate(setChldNodeImgIndex), new object[] { rootNodes[i], 2 }); //yellow
downServers++;
}
}
}
if (downServers == childNodes.Count)
{
//rootNodes[i].ImageIndex = 4; //fatal red
treeViewSrv.Invoke(new setChldNodeImgIndexDelegate(setChldNodeImgIndex), new object[] { rootNodes[i], 4 });
}
}
}
private void setServersCount(int idx, int cnt)
{
string[] spltNodeText = treeViewSrv.Nodes[idx].Text.Split(' ');
treeViewSrv.Nodes[idx].Text = string.Format("{0} ({1})", spltNodeText[0], cnt);
}
private void setChldNodeImgIndex(TreeNode tNode, int imgIdx)
{
tNode.ImageIndex = imgIdx;
}

callback and onthreadfinish

I am just asking for the cleanest way to have a callback to update the GUI when a thread is running and a method that will be called when the thread has finished.
So in my example, I have a Counter class (this is the task), and 2 delegates, 1 for callback, 1 for finishing of the thread.
It all works fine, but I have the feeling that this is not the best/cleanest/.. way to do it.
any suggestions would be greatly appreciated, I'm having a hard time understanding the concept of a delegate and threads threads since I haven't started programming that long ago.
The counter class
class Counter
{
private PrintCallback cb;
private OnActionFinish oaf;
public void SetCallback(PrintCallback c)
{
this.cb = c;
}
public void SetOnFinished(OnActionFinish f)
{
this.oaf = f;
}
public void Count()
{
for (int i = 0; i < 1000; i++)
{
cb(i);
}
oaf();
}
}
And my main form
public delegate void PrintCallback(int i);
public delegate void OnActionFinish();
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
PrintCallback cb = new PrintCallback(Print);
OnActionFinish otf = new OnActionFinish(Finished);
Counter c = new Counter();
c.SetCallback(cb);
c.SetOnFinished(otf);
Thread t = new Thread(c.Count);
t.Start();
label1.Text = "Thread started";
}
private void Print(int i)
{
if (textBox1.InvokeRequired)
{
textBox1.Invoke((MethodInvoker)delegate {
textBox1.Text += i + "\r\n";
});
}
else
{
textBox1.Text += i + "\n";
}
}
private void Finished()
{
if (label1.InvokeRequired)
{
label1.Invoke((MethodInvoker)delegate {
label1.Text = "Thread finished";
textBox1.SelectionStart = textBox1.Text.Length;
textBox1.ScrollToCaret();
});
}
else
{
label1.Text = "Thread finished";
}
}
To refactor your code, using your own techniques, it could become something like this:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
label1.Text = "Thread started";
Counter c = new Counter(Print, Finished);
new Thread(new ThreadStart(c.Count)).Start();
}
private void Print(int i)
{
if (InvokeRequired)
Invoke(new Action<int>(Print), i);
else
textBox1.Text += i + "\r\n";
}
private void Finished()
{
if (InvokeRequired)
{
Invoke(new Action(Finished));
}
else
{
label1.Text = "Thread finished";
textBox1.SelectionStart = textBox1.Text.Length;
textBox1.ScrollToCaret();
}
}
}
class Counter
{
private readonly Action<int> _output;
private readonly Action _finished;
public Counter(Action<int> output, Action finished)
{
_output = output;
_finished = finished;
}
public void Count()
{
for (int i = 0; i < 1000; i++)
_output(i);
_finished();
}
}

Updating Progress Bar from Thread in C#

public class Consumer
{
Queue<int> queue;
Object lockObject;
public Consumer(Queue<int> queue, Object lockObject)
{
this.queue = queue;
this.lockObject = lockObject;
}
public void consume(string filepath)
{
int item = 0;
while (true)
{
lock (lockObject)
{
if (queue.Count == 0)
{
Monitor.PulseAll(lockObject);
continue;
}
item = queue.Dequeue();
if (item == 0)
{
break;
}
//do some staff
}
}
}
}
public class Producer
{
Queue<int> queue;
Object lockObject;
public int ProgressPercent = 0;
int TotalProducedElements = 0;
public bool check1 = false;
public Producer(Queue<int> queue, Object lockObject)
{
this.queue = queue;
this.lockObject = lockObject;
}
private bool IsPrime(int num)
{
if (num == 0)
return true;
num = Math.Abs(num);
for (int i = 2; i <= Math.Sqrt(num); i++)
if (num % i == 0)
return false;
return true;
}
public void produce(int target)
{
try
{
int seq = 0;
while (seq++ < target)
{
lock (lockObject)
{
int item = seq;
if (IsPrime(item))
{
queue.Enqueue(item);
}
TotalProducedElements++;
ProgressPercent = seq;
if (queue.Count == 0)
{
Monitor.PulseAll(lockObject);
}
}
}
queue.Enqueue(0);
}
catch (Exception e)
{
}
}
}
}
private void Start_Click(object sender, EventArgs e)
{
Object lockObj = new object();
Queue<int> queue = new Queue<int>();
Producer p = new Producer(queue, lockObj);
Consumer c = new Consumer(queue, lockObj);
int target = int.Parse(TargetText.Text);
string path = Path.Text;
Thread Thread1 = new Thread(() => p.produce(target));
Thread Thread2 = new Thread(()=>c.consume(path));
Thread1.Start();
Thread2.Start();
progressBar1.Maximum = target;
while(true)
{
if(p.ProgressPercent==0)
{
Thread.sleep(1000);
}
else
{
progressBar1.Value=p.ProgressPercent;
}
}
}
I have two classes working on same queue. one to produce a set of integers and the second is to consume that integers from queue.
And during all this I want to update my progress bar by that percentage.
So how to update progress bar from consumer without blocking for my GUI?
Note I have used progressbar.Invoke and delegate but didn't work.
You need two things. First is obviously a thread, the other is invoker.
Invokers will let you change window's controls from inside of a thread. Here's the example:
Invoke((MethodInvoker)delegate
{
Label1.text = "asd";
}
Threads are ran like this:
some_thread = new Thread
(delegate()
{
{
//some_thread code
}
});
some_thread.Start();
You also need to add using System.Threading; at the beginning of the file.
So, the final code should look like this:
some_thread = new Thread
(delegate()
{
{
for(;;)
{
Invoke((MethodInvoker)delegate
{
//update the progress bar here
}
}
}
});
some_thread.Start();
You could use a BackgroundWorker (System.ComponentModel) to accomplish this easily. Take a look at:
http://www.codeproject.com/Tips/83317/BackgroundWorker-and-ProgressBar-demo
Clear:
while(true)
{
if(p.ProgressPercent==0)
{
Thread.sleep(1000);
}
else
{
progressBar1.Value=p.ProgressPercent;
}
}
Replace:
ProgressPercent = seq;
to
UpdateProgres(seq);
Add:
public void UpdateProgres(int _value)
{
if (InvokeRequired)
{
Invoke(new Action<int>(UpdateProgres), _value);
return;
}
progressBar1.Value = _value;
}
Summary your needed function;
public void UpdateProgres(int _value)
{
if (InvokeRequired)
{
Invoke(new Action<int>(UpdateProgres), _value);
return;
}
progressBar1.Value = _value;
}
Or need Lock the value. For Example;
while(true)
{
lock (p.ProgressPercent)
{
int _p = p.ProgressPercent;
}
if(_p==0)
{
Thread.sleep(1000);
}
else
{
progressBar1.Value=_p;
}
}

EF context and multi-threading

I was wondering if somebody explain me what is wrong in my code.
I run timer in extra thread. In timer function I work with EF context. I see that timer function has worked 6 times and I especially set interval in 3 seconds and take only 100 rows but in my DB I see only one work.
So where is my error?
namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
private static int cnt = 0;
private Thread thread;
public Form1()
{
InitializeComponent();
}
private void StartThread()
{
var timer = new System.Timers.Timer();
timer.Elapsed += new System.Timers.ElapsedEventHandler(ProcessDb);
timer.Interval = 3000;
timer.Start();
}
public void ProcessDb(object sender, System.Timers.ElapsedEventArgs e)
{
cnt++;
ConnService serv = new ConnService();
serv.UpdateConnections(cnt);
}
private void button1_Click(object sender, EventArgs e)
{
thread = new Thread(StartThread);
thread.Start();
Thread.Sleep(20000);
}
}
public class MyqQueue
{
public static Stack<int> myStack = new Stack<int>();
public static Stack<int> myStack2 = new Stack<int>();
}
}
namespace WindowsFormsApplication2
{
class ConnService
{
public ConnService()
{
cnt++;
}
private static int cnt;
public void UpdateConnections(int second)
{
MyqQueue.myStack.Push(second);
DjEntities ctx = new DjEntities();
var entities = ctx.Connections.Take(100).Where(c => c.State == null);
foreach (var connection in entities)
{
connection.State = second;
}
if (second == 1)
Thread.Sleep(7000);
MyqQueue.myStack2.Push(second);
ctx.SaveChanges();
}
}
}
ctx.Connections.Take(100).Where(c => c.State == null)
Should be changed to
ctx.Connections.Where(c => c.State == null).Take(100)
Your first query translates to first take 100 without filtering and then apply filter.
Second query I have written will take items filtered and then take top 100.

Categories

Resources