I'm trying to convert my foreach method to multi-threading.
I have a datagridview and the method gets the value from cell[0] (which contains a url) and sends it to another method which works with httpwebrequest.
public void UrlCheck()
{
foreach (DataGridViewRow row in dataUrlList.Rows)
{
string url= row.Cells[0].Value.ToString();
try
{
string get = getHtml(url);
//work with get string removed
if()
{
row.Cells[1].Value = "page info here";
}
else
{
row.Cells[1].Value = "error info here";
}
}
catch
{
}
}
MessageBox.Show("Done.");
}
The above code is working without any problem but sequentially.
Then with this i've tried to convert this code to that to be multithreaded :
Button :
private void button9_Click(object sender, EventArgs e)
{
int threadcount = Convert.ToInt32(numThreadSearch.Value);
ThreadForSearch = new Thread[threadcount];
checkingForSearch = dataUrlList.Rows.Count;
isRunningForSearch = true;
beenCheckedForSearch = 1;
for (int i = 0; i <= threadcount - 1; i++)
{
ThreadForSearch[i] = new Thread(new ParameterizedThreadStart(MultiThreadMet));
ThreadForSearch[i].Start(i);
}
}
and the multi-threaded method is here :
public void MultiThreadMet (object IndexForSearch)
{
int index = (int)IndexForSearch;
DataGridViewRow row = dataUrlList.Rows[index];
while (isRunningForSearch)
{
try
{
if (beenCheckedForSearch >= checkingForSearch)
{
isRunningForSearch = false;
}
if (index >= dataUrlList.Rows.Count)
{
ThreadForSearch[index].Abort();
}
//For just test i'm trying to add "test" in every cell[1] in datagridview
dataUrlList.Invoke(new Action(() => row.Cells[1].Value = "test"));
beenCheckedForSearch++;
}
catch
{
}
}
ThreadForSearch[1].Abort();
}
It gets the thread count to run from a numericUpDown control and if I choose the value 10 from the numericUpDown it puts "test" text in the first 10 cells of datagridview and then stops, if i choose 4(or 2,5,7), then it puts the "test" text in the first 4(2,5,7) cells and stops.
It doesn't continue to the next rows after the thread is finished. So I'm trying to fire 5 threads (I always choose it from numericUpDown) and when a thread finish its work, it must go to next row.. How can i solve this problem ?
Also these variables have been declared:
Thread[] ThreadForSearch;
int beenCheckedForSearch;
int checkingForSearch;
private bool isRunningForSearch;
from output screen i'm getting
Exception thrown: 'System.Threading.ThreadAbortException' in mscorlib.dll
Thanks.
The index counter was not incremented in the thread method.
There is still an overlapping problem in the program that I will let you fix. Assuming there are 50 urls, the problem is that thread 1 will update cells 0 to 49, thread 2 will update cells 1 to 49, and so on.
public void MultiThreadMet (object IndexForSearch)
{
int index = (int)IndexForSearch;
while (isRunningForSearch)
{
try
{
if (index >= dataUrlList.Rows.Count)
{
return;
}
DataGridViewRow row = dataUrlList.Rows[index];
index++;
//For just test i'm trying to add "test" in every cell[1] in datagridview
dataUrlList.Invoke(new Action(() => row.Cells[1].Value = "test"));
}
catch
{
}
}
}
Related
I have a timer event that does several things. One item I am trying to get it to do is to programmatically remove the CheckListBox items that are checked once the timer hits the completed action I am performing.
This is the code for the timer and what I have tried to do.
private void timer1_Tick(object sender, EventArgs e)
{
string s;
if (DbFirmwareUpdateComplete.WaitOne(1))
{
DbFirmwareUpdateComplete.Reset();
mnuLoadKeyFile.Enabled = true;
}
if (DbUpdateComplete.WaitOne(1))
{
DbUpdateComplete.Reset();
mnuLoadKeyFile.Enabled = true;
btnLoad.Enabled = true;
}
if (CacheComplete.WaitOne(1))
{
CacheComplete.Reset();
btnLoad.Enabled = true;
}
if (UpdateRunning)
{
bool UpdateDone = true;
int StillActive = 0;
// loop through all active jobs to check if all have completed
foreach (clsCnaPair cna in ActiveJobs)
{
if (cna.Job.JobComplete == false)
{
UpdateDone = false;
StillActive++;
}
else
{
if (cna.Job.UpdateSuccess)
{
// Update color of CLB.Items.Selected if success.
int count = CLB.Items.Count;
for (int index = count; index > 0; index--)
{
if(CLB.CheckedItems.Contains(CLB.Items[index-1]))
{
CLB.Items.RemoveAt(index - 1);
}
}
}
else
{
// Update color of CLB.Items.Selected if failed.
}
}
}
if (UpdateDone)
{
UpdateRunning = false;
log("All Update jobs have finished.");
}
if (ckTop.Checked == true)
{
ckTop.Checked = false;
}
else
{
ckTop.Checked = false;
}
When I run the program and it hits this piece;
if (cna.Job.UpdateSuccess)
{
// Update color of CLB.Items.Selected if success.
int count = CLB.Items.Count;
for (int index = count; index > 0; index--)
{
if(CLB.CheckedItems.Contains(CLB.Items[index-1]))
{
CLB.Items.RemoveAt(index - 1);
}
}
}
I get an error:
System.ArgumentOutOfRangeException: InvalidArgument=Value of '-1' is not valid for 'index'.
Parameter name: index
The Error occurs after this piece of code;
private void CLB_SelectedIndexChanged(object sender, EventArgs e)
{
// One of the CNA IPs was selected. sender is the CheckedListBox.
// Here we want to display its fingerprint in the text box, or if the push is running, the status.
// get the CnaPair class represented by this IP:
clsCnaPair CnaPair = (clsCnaPair)CLB.Items[CLB.SelectedIndex];
// Display the corresponding fingerprint string in the editBox:
if (CnaPair.Job != null) txtStatus.Text = CnaPair.Job.GetStatus();
else txtStatus.Text = CnaPair.GetInfo();
}
Or more specifically at the line:
clsCnaPair CnaPar = (clsCnaPair)CLB.Items[CLB.SelectedIndex];
What am I missing? Searching google, shows the way I am doing the remove is consistent with the examples found there.
Thanks,
It's dangerous to modify the contents of the ChecklistBox inside a loop when the loop conditions depend on the contents of the ChecklistBox. Once you call RemoveAt(), the CheckedItems list and the CLB.Items.Count has changed and you will have a problem. In this case, the loop fired the SelectedIndexChanged() event with an invalid Index (-1).
Better to do this in a do-while loop:
bool done;
do
{
done = true;
for (int index = CLB.Items.Count; index > 0; index--)
{
if(CLB.CheckedItems.Contains(CLB.Items[index-1]))
{
CLB.Items.RemoveAt(index - 1);
done = false;
break;
}
}
}while(!done);
This way, every time an item is removed, you break out and start the loop all over again.
After some experimentation, I commented out the CLB_SelecteIndexChanged code and it now completes with the original code.
That leaves one issue. What is the work around with the CLB_SelectedIndexChanged code left in. I will work on that one more and see f I can figure it out with what you guys have provided.
Thanks to both m.rogalski and mcNets.
I have a table with 100,000 records and i have a method (using entity framework) that retrieve 10 records, i give it how many records skip to get the next 10 records.
List<Item> GetRecords(int skip = 0);
I load the first 10 records on a list, and set it as datasource of the UltraGrid, how can i call the method to get the next 10 records and add it to the UltraGrid when the scroll reaches the bottom or is near to reach the bottom?
I have a solution for your requirement. Hope this helps you..
First, create a windows form named "test" (say)..
Add a ultraGrid in the form..
check the following code:
public partial class test : Form
{
DataTable dtSource = new DataTable();
int takecount = 50;
int skipcount = 0;
DataTable dtResult;
// CONSTRUCTOR
public test()
{
InitializeComponent();
// Fill Dummy data here as datasource...
dtSource.Columns.Add("SNo", typeof(int));
dtSource.Columns.Add("Name", typeof(string));
dtSource.Columns.Add("Address", typeof(string));
int i = 1;
while (i <= 500)
{
dtSource.Rows.Add(new object[] { i, "Name: " + i, "Address " + i });
i++;
}
dtResult = dtSource.Copy();
dtResult.Clear();
}
// ON FORM LOAD FUNCTION CALL
private void test_Load(object sender, EventArgs e)
{
ultraGrid1.DataSource = dt_takeCount();
ultraGrid1.DataBind();
}
private DataTable dt_takeCount()
{
if (dtSource.Rows.Count - skipcount <= takecount)
{
takecount = dtSource.Rows.Count - skipcount;
}
foreach (var item in dtSource.AsEnumerable().Skip(skipcount).Take(takecount))
{
dtResult.Rows.Add(new object[] { item.Field<int>("SNo"), item.Field<string>("Name"), item.Field<string>("Address") });
}
if (dtSource.Rows.Count - skipcount >= takecount)
{
skipcount += takecount;
}
return dtResult;
}
// EVENT FIRED WHEN ON AFTERROWREGIONSCROLL
private void ultraGrid1_AfterRowRegionScroll(object sender, Infragistics.Win.UltraWinGrid.RowScrollRegionEventArgs e)
{
int _pos = e.RowScrollRegion.ScrollPosition;
if (ultraGrid1.Rows.Count - _pos < takecount)
{
dt_takeCount();
}
}
}
Above code is all that works..
--> "ultraGrid1_AfterRowRegionScroll" function is "AfterRowRegionScroll" event function
--> But be sure that when you choose "takecount", it generates scrollbar,
--> when you run above code... the row will be updated by 50 when you scroll,, till 500th row.. because it is the last row.
I have a simple c# application which display data from a access database
private void DisplayRow(int rowIndex)
{
// Check that we can retrieve the given row
if (myDataTable.Rows.Count == 0)
return; // nothing to display
if (rowIndex >= myDataTable.Rows.Count)
return; // the index is out of range
// If we get this far then we can retrieve the data
try
{
DataRow row = myDataTable.Rows[rowIndex];
SurnametextBox.Text = row["Surname"].ToString();
FirstNametextBox.Text = row["Firstname"].ToString();
PhonetextBox.Text = row["Phone"].ToString();
ContactTypetextBox.Text = row["ContactType"].ToString();
}
catch (Exception ex)
{
MessageBox.Show("Error in DisplayRow : \r\n" + ex.Message);
}
}
Now it shows the first record in the database and that is it, i have next and back buttons in place in order to click through the data but what code is needed to display the next record?
Create a class variable, so you can remember which row index you are having currentlly. So when you click NEXT, add +1 to this variable, and when pressing PREVIOUS subtract from it (-1).
And use the variable afterwards.
class Form1
{
int counter;
void ButtonForth_Click()
{
counter++;
YourMethod();
}
void ButtonBack_Click()
{
counter--;
YourMethod();
}
void YourMethod()
{
DataRow row = table.Rows[counter];
// more code...
}
}
RhysW: thx for pointing that out. I was just simply showing the basics of the pattern code. Sure there is plenty to do to make it work smoothly.
This is how it can be done approx:
class Form1
{
int counter;
DataTable table; //class variable so we can access to the reference from all over the class
void ButtonForth_Click()
{
if(counter < table.Rows.Count)
counter++;
YourMethod();
}
void ButtonBack_Click()
{
if(counter > 0)
counter--;
YourMethod();
}
void YourMethod()
{
DataRow row = table.Rows[counter];
if(row.Count > 0 )
{
SurnametextBox.Text = row["Surname"].ToString();
FirstNametextBox.Text = row["Firstname"].ToString();
PhonetextBox.Text = row["Phone"].ToString();
ContactTypetextBox.Text = row["ContactType"].ToString();
}
//no need of try, catch block, is there are all the names correct (textobxes, and columns of datatable)
}
}
Make sure rowIndex is getting incremented before this function is called. Otherwise the code looks good.
I have an application which has to monitor 211 rods, and every 5 seconds it will update 2 ListBox controls, each one containing either the inserted rods or the removed ones. When I manually use the button for inserting/removing rods the code executes perfectly and the ListBoxes update properly. When I use the global button which inserts all 211 one of the ListBox controls stops working properly.
The code for ListBox update
bool IClear = true, RClear = true;
for (int foo = 0; foo < Rods.Count; foo++)
{
if (Rods[foo].State == RodState.Inserted)
{
UpdateRodList update = new UpdateRodList(UpdateIRodUI);
if (IClear)
{
InsertedRods.Dispatcher.BeginInvoke(update, System.Windows.Threading.DispatcherPriority.Normal, foo, true);
IClear = false;
}
else
{
InsertedRods.Dispatcher.BeginInvoke(update, System.Windows.Threading.DispatcherPriority.Normal, foo, false);
}
}
if (Rods[foo].State == RodState.Removed)
{
UpdateRodList update = new UpdateRodList(UpdateRRodUI);
if (RClear)
{
RemovedRods.Dispatcher.BeginInvoke(update, System.Windows.Threading.DispatcherPriority.Normal, foo, true);
RClear = false;
}
else
{
RemovedRods.Dispatcher.BeginInvoke(update, System.Windows.Threading.DispatcherPriority.Normal, foo, false);
}
}
}
The code for the insert button (the remove one is similar)
Int32[] RodsID = null;
bool bParsed = false;
if (RemovingRods_.Text.Contains("*"))
{
RodsID = new Int32[211];
for (int i = 0; i < 211; i++)
{
RodsID[i] = i;
}
RemovingRods_.Text = "";
bParsed = true;
}
if (RemovingRods_.Text.Contains("-"))
{
string stext = RemovingRods_.Text;
Int32 a = Int32.Parse(RemovingRods_.Text.Substring(0, RemovingRods_.Text.IndexOf("-")));
Int32 b = Int32.Parse(RemovingRods_.Text.Substring(RemovingRods_.Text.IndexOf("-") + 1));
RodsID = new Int32[b - a];
for (int i = 0; i < b - a; i++)
{
RodsID[i] = i + a;
}
RemovingRods_.Text = "";
bParsed = true;
}
if (!bParsed)
{
string[] RodsID_;
char[] split = { ' ' };
RodsID_ = RemovingRods_.Text.Split(split);
RemovingRods_.Text = "";
RodsID = new Int32[RodsID_.Length];
for (int i = 0; i < RodsID_.Length; i++)
{
RodsID[i] = Int32.Parse(RodsID_[i]);
}
}
foreach (int numb in RodsID)
{
if (Rods[numb].Type == "Control Rod")
{
ControlRod Rod = new ControlRod();
Rod.Number = numb;
Rod.RodState = RodState.Changing;
RemovingCRods.Add(Rod);
}
if (Rods[numb].Type == "Shortened Control Rod")
{
ShortenedControlRod Rod = new ShortenedControlRod();
Rod.Number = numb;
Rod.RodState = RodState.Changing;
RemovingSRods.Add(Rod);
}
if (Rods[numb].Type == "Automated Control Rod")
{
// Automated Rods -- NO MANUAL CONTROL
}
}
And the global button code
try
{
Int32[] RodsID = null;
string text = "0-211";
RodsID = new Int32[211];
for (int i = 0; i < 211; i++)
{
RodsID[i] = i;
}
foreach (int numb in RodsID)
{
if (Rods[numb].Type == "Control Rod")
{
ControlRod Rod = new ControlRod();
Rod.Number = numb;
Rod.RodState = RodState.Changing;
InsertingCRods.Add(Rod);
}
if (Rods[numb].Type == "Shortened Control Rod")
{
ShortenedControlRod Rod = new ShortenedControlRod();
Rod.Number = numb;
Rod.RodState = RodState.Changing;
InsertingSRods.Add(Rod);
}
if (Rods[numb].Type == "Automated Control Rod")
{
AutomatedControlRod Rod = new AutomatedControlRod();
Rod.Number = numb;
Rod.RodState = RodState.Changing;
InsertingARods.Add(Rod);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
What happens is when I press the global one, the Removed Rods ListBox will have all the rods as it should, and the Inserted Rods ListBox will contain the rods that were inserted before I pressed the button. It's as if when I pressed this button, this control doesn't update. P.S. If I remove rods manually using the button or insert it works perfectly.
As for the code requested by Marko:
private void UpdateIRodUI(Int32 foo, Boolean clear)
{
if (clear)
{
InsertedRods.Items.Clear();
}
InsertedRods.Items.Add(Rods[foo].Number + " : " + Rods[foo].Type + " (" + foo.ToString() + ")");
}
private void UpdateRRodUI(Int32 foo, Boolean clear)
{
if (clear)
{
RemovedRods.Items.Clear();
}
RemovedRods.Items.Add(Rods[foo].Number + " : " + Rods[foo].Type + " (" + foo.ToString() + ")");
}
Update: I have put the update ListBox code in a seperate function and took Marko's advice and also put in a function the InsertRods. Everything works fine now, but it seems that after I press the "emergency" button the InsertedRods ListBox updates and works just fine but RemovedRods just stops updating, unless I do it manually (it's supposed to update every 5 seconds through a Tick event). I even tried inserting all the rods, updating the ListBoxes and the clearing the "faulty" ListBox and still nothing, same result.
I just took a quick glance of your posted code without focusing very deeply on it and a couple of questions popped into mind:
1) You posted your code for ListBox update, but it's unclear from the other two code pieces that where do you call the ListBox update method?
2) The code that you posted for "insert button" looks more like the code from the "remove button", because of Removing_Rods.Add()... But why do you duplicate your insert/remove button code in your global button code? Why not have an insert method, that both the insert button and global (insert) button call? And the same for remove. If you need to slightly alter the code based on whether the caller is the insert button or the global button, you can pass in a variable and check it inside the insert method.
3) Have you tried debugging your code? As in whether the listbox update method is called when the global button code is executed...
I have a query to a database on a seperate thread and on the return I invoke a threadsafe call. But an exception is created - what am I doing wrong?
Note I also populate the list view but have left it out for clarity
private void ThreadSafeListView()
{
if (this.listView1.InvokeRequired)
{
try
{
ThreadSafe Operation d = new ThreadSafeOperation(ThreadSafeListView);
this.Invoke(d );
}
catch { }
}
else
{
listView1.Columns[0].AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);
listView1.Columns[1].AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);
listView1.Columns[2].AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);
listView1.Columns[3].AutoResize(ColumnHeaderAutoResizeStyle.HeaderSize);
listView1.Columns[4].AutoResize(ColumnHeaderAutoResizeStyle.HeaderSize);
}
}
Exception Details=
InvalidOperationException :
"Cross-thread operation not valid: Control 'listView1' accessed from a thread other than the thread it was created on."
What exception is thrown? If it's still a thread-safe exception, then it may be because you're not invoking from the item that requires invocation.
Try:
listView1.Invoke(d)
rather than
this.Invoke(d)
In theory this is the form, so it should work, but without more information about the exception this is my only guess.
I give up...I'm just spinning my wheels so instead I...
void listView1_Resize(object sender, EventArgs e)
{
listView1.Columns[0].AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);
listView1.Columns[1].AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);
listView1.Columns[2].AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);
listView1.Columns[3].AutoResize(ColumnHeaderAutoResizeStyle.HeaderSize);
}
you can do this:
private void listViewEx_ColumnWidthChanging(object sender, ColumnWidthChangingEventArgs e)
{
e.Cancel = true;
e.NewWidth = 60;
}
//Here ilv is the List, who's column are to be resized
//Get the current column widths
ArrayList widths = new ArrayList();
foreach (ColumnHeader ch in ilv.Columns)
{
widths.Add(ch.Width);
}
//Get the total width of all the columns
int total_width = 0;
for (int i = 0; i < widths.Count; i++)
{
total_width += (int)widths[i];
}
//Calculate percentages and resize the columns.
for (int i = 0; i < widths.Count; i++)
{
double c_width = (int)widths[i];
double pect = (c_width / total_width);
//get the new width, leave out 25 pixels for scrollbar
double n_width = (ilv.Width - 25) * pect;
n_width = Math.Round(n_width, 0);
//MessageBox.Show(c_width + " - " + pect + " - " + n_width);
ilv.Columns[i].Width = (int)n_width;
}
you can do the following code...
first get the value of listview onload..
then store it in a variable the do this code in the ColumnWidthChanging event property like this...
e.cancel = true;
e.NewWidth = // the declared variable in which you store the list view with value in the onload function of the form
Example is like this
int a = 100;
e.cancel =true;
e.e.NewWidth = a;
just like that