This is the first time I'm attempting multiple threads in a project so bear with me. The idea is this. I have a bunch of documents I need converted to pdf. I am using itextsharp to do the conversion for me. When run iteratively, the program runs fine but slow.
I have a list of items that need to be converted. I take that list and split it into 2 lists.
for (int i = 0; i < essaylist.Count / 2; i++)
{
frontessay.Add(essaylist[i]);
try
{
backessay.Add(essaylist[essaylist.Count - i]);
}
catch(Exception e)
{
}
}
if (essaylist.Count > 1)
{
var essay1 = new Essay();
Thread t1 = new Thread(() => essay1.StartThread(frontessay));
Thread t2 = new Thread(() => essay1.StartThread(backessay));
t1.Start();
t2.Start();
t1.Join();
t2.Join();
}
else
{
var essay1 = new Essay();
essay1.GenerateEssays(essaylist[1]);
}
I then create 2 threads that run this code
public void StartThread(List<Essay> essaylist)
{
var essay = new Essay();
List<System.Threading.Tasks.Task> tasklist = new List<System.Threading.Tasks.Task>();
int threadcount = 7;
Boolean threadcomplete = false;
int counter = 0;
for (int i = 0; i < essaylist.Count; i++)
{
essay = essaylist[i];
var task1 = System.Threading.Tasks.Task.Factory.StartNew(() => essay.GenerateEssays(essay));
tasklist.Add(task1);
counter++;
if (tasklist.Count % threadcount == 0)
{
tasklist.ForEach(t => t.Wait());
//counter = 0;
tasklist = new List<System.Threading.Tasks.Task>();
threadcomplete = true;
}
Thread.Sleep(100);
}
tasklist.ForEach(t => t.Wait());
Thread.Sleep(100);
}
For the majority of the files, the code runs as it should. However, for example I have 155 items that need to be convereted. When the program finishes and I look at the results I have 149 items instead of 155. It seems like the results are something like the total = list - threadcount. In this case its 7. Any ideas on how to correct this? Am I even doing threads/tasks correctly?
Also the essay.GenerateEssays code is the actual itextsharp that converts the info from the db to the actual pdf.
How about using TPL. It seems that all your code can be replaced with this
Parallel.ForEach(essaylist, essay =>
{
YourAction(essay);
});
Related
I'm working on a WPF-MVVM project and I need to implement asynchronous infinite loops in some background threads. What I have done in the ViewModel is
public TestVM()
{
LineIO_Task();
//some other work
}
and LineIO_Task is defined as
public void LineIO_Task()
{
for (int i = 0; i < 7; i++)
{
Task GetP = new Task(() => { EnPost(Lines[i]); }, TaskCreationOptions.LongRunning);
GetP.Start();
}
}
Lines is an ObservableCollection that is initialized in TestVm.
And EnPost is defined as
public async void EnPost(Line l)
{
int last = 0;
while (true)
{
// do the work in the loop
int pno = l.Com.ReadPostNo();//read a serial port
if (pno != 0 && pno != last)
{
log.WriteLog(pno + " to " + l.ToString());
Dispatcher.Invoke(() =>
{
// update the UI
l.Posts.First(x => x.IsValid).Num = pno;
l.Posts.First(x => x.IsValid).IsValid = false;
LocalDb.InsertPost(l.Num, AssignedPost.ToList().Find(x => x.Num == pno));
});
pno = last;
}
await Task.Delay(500);
}
}
I've tried Task.Run(() => Method()), Task.Factory.StartNew(() => Method()),,async Task EnPost() and using a System.Timers.Timer. But no matter which way I use, the EnPost method just doesn't run. I put break-points in the method. It doesn't hit there. Am I using Task wrong?
I'm guessing this is a "captured variable" issue; try:
for (int i = 0; i < 7; i++)
{
int index = i;
Task GetP = new Task(() => { EnPost(Lines[index]); }, TaskCreationOptions.LongRunning);
GetP.Start();
}
(fixed by question edit) Note however that using the thread-pool for a very long lived task is not a good idea. You might want to use a full thread instead. Also; your TaskDelay may want to be inside the while loop, in which case you can ignore the previous comment, as it is no longer actually a very long lived single piece.
Thanks for #Marc and #Brian's answer. They mention the"captured variable" issue, so I tried
foreach (Line l in Lines)
{ ... }
It works finally.
I need to create a program scraping a website.
And I did use Thread to solve.
Example:
I have 100 pages and I need divide it, instead of get each page I need custom Thread number to get page:
2 threads - 50 pages/thread
4 threads - 25 pages/thread
I tried my code below, however when to the last page of each thread that very slow.
Before I ask I used to find the way to solve but I can't, therefore I need help.
int so_thread = 10;//thread number
int page_du = 0;
List<NameValueCollection> List_item = new List<NameValueCollection>();
Thread[] threads = new Thread[so_thread];
int dem = 0;
await Task.Run(() =>
{
for (int i = 1; i <= so_thread; i++)
{
if ((Int32.Parse(o_sopage.Text) % so_thread) != 0 && i == so_thread)
{
page_du = Int32.Parse(o_sopage.Text) % so_thread;//Int32.Parse(o_sopage.Text) == page number need get
}
threads[i - 1] = new Thread((object data) =>
{
Array New_Data = new object[2];
New_Data = (Array)data;
int _i = (int)New_Data.GetValue(0);
int _pagedu = (int)New_Data.GetValue(1);
int page_per_thread = Int32.Parse(o_sopage.Text) / so_thread;//Int32.Parse(o_sopage.Text) == page number need get
for (int j = ((page_per_thread * _i) - page_per_thread) + 1; j <= ((page_per_thread * _i) + _pagedu); j++)
{
//MessageBox.Show(j.ToString());
var TG = ebay.GetPage(j);
lock (List_item)
{
List_item.AddRange(TG);
dem++;
progressBar1.Invoke((MethodInvoker)delegate
{
progressBar1.Value = dem;
});
}
}
});
object DATA = new object[2] { i, page_du };
threads[i - 1].Start(DATA);
}
});
Use Parallel.ForEach instead of creating the threads on your own.
Parallel.ForEach(yourCollection, () => { /* your code here */});
I've been trying to implement multi threading which looks something like this:
static void Main(string[] args)
{
List<Task> tskList = new List<Task>();
for (int i = 0; i < 100; i++)
{
Task taskTemp = new Task(() => { Display(i); });
taskTemp.Start();
tskList.Add(taskTemp);
//Thread.Sleep(10);
}
Task.WaitAll(tskList.ToArray());
}
public static void Display(int value)
{
Thread.Sleep(1000);
Console.WriteLine(value);
}
Without the Thread.Sleep(10) part, I get output printed as 100 times "100" instead of 0 to 99 which I'm getting with that sleep time of 10 ms.
My guess is that this could be happening because of the time required to schedule the thread by the system and by the time the thread is about to actually start, the value has reached 100.
If I put enough wait time (say 1000 ms instead of 10), will it be guaranteed to not have this problem? Or should I suspect that the system may take even more time to schedule the thread when CPU utilization is too much? What is the best way to solve this problem?
Thanks in advance for any inputs!
you should add a local variable to hold 'i', such as :
for (int i = 0; i < 100; i++)
{
var t = i;
Task taskTemp = new Task(() => { Display(t); });
taskTemp.Start();
tskList.Add(taskTemp);
//Thread.Sleep(10);
}
Just make a copy of "i" to "i1" and use it as local variable. "i" is always changed, thats why you get 100 100 100....:
private static void Main(string[] args)
{
var tskList = new List<Task>();
for (var i = 0; i < 100; i++)
{
var i1 = i;
var taskTemp = new Task(() => { Display(i1); });
taskTemp.Start();
tskList.Add(taskTemp);
}
Task.WaitAll(tskList.ToArray());
}
public static void Display(int value)
{
Thread.Sleep(1000);
Console.WriteLine(value);
}
Thread[] threads = new Thread[12];
int temp;
for (int i = 0; i < threads.Length - 1; i++)
{
temp = i;
threads[temp] = new Thread(new ThreadStart(()=> test(test1[temp],"start", temp)));
threads[temp].Start();
//threads[temp].Join();
}
for(int i=0; i<threads.Length-1; i++)
{
threads[i].Join();
}
//Need to capture the response returned from method executed"test1" in thread.
You could use a Task<T> (if you're on .NET 4+), which has a return value. You could also use events to get notified when the thread is done with doing whatever it does and get the returned value that way.
I would use Microsoft's Reactive Framework for this. NugGet "Rx-Main".
var query =
Observable
.Range(0, 12)
.SelectMany(n => Observable
.Start(() => new
{
n,
r = test(test1[n], "start", n)
}))
.ToArray()
.Select(xs => xs
.OrderBy(x => x.n)
.Select(x => x.r)
.ToArray());
query.Subscribe(rs =>
{
/* do something with the results */
});
You could start the thread using another ctor overload where you can start the thread and pass an object to that thread. The thread would then save the result in a field of that object. The main thread could after the call to Join retrieve the results from all those objects. You could have an array of 12 objects each of them passed to one thread. Or you could have an array of 12 classes, each class encapsulating one thread and the corresponding object that wraps the result:
public class ThreadResult
{
public int Result {get; set;}
}
However, today you have better choices than raw threads. Take a look at TPL (Task Parallel Library) and async / await in C#.
You can also use a shared state, in this case you have to lock every access to the shared objects inside the thread method:
Thread[] threads = new Thread[12];
int temp;
string msg = "";
List<string> results = new List<string>();
for (int i = 0; i < threads.Length; i++)
{
temp = i;
threads[temp] = new Thread(() =>
{
lock (results)
{
lock (msg)
{
msg = "Hello from Thread " + Thread.CurrentThread.ManagedThreadId;
results.Add(msg);
}
}
});
threads[temp].Start();
}
for (int i = 0; i < threads.Length; i++)
{
threads[i].Join();
}
This question already has an answer here:
Thread alters passed Int, if start() is called separately
(1 answer)
Closed 7 years ago.
I have no idea what is going on in this. I'm trying to test thread safety of a class by spawning 100 threads to access it constantly, but it seems my anonymous method parameters are changing themselves to values they should never be and I'm confused as to why. As I have no idea what's going on, I'll just post all the functions involved in testing. Somehow I'm ending up with "Thread 98" getting the parameter "num = 100"... That shouldn't be possible and I have no idea what kind of hokey pokey is going on that is changing the integer. (in method "ThreadWriting(int num)" you'll see the point where I check for "num" to equal 100, where I put a break point to catch the offending thread in the act, and it breaks every time. Otherwise it throws an "IndexOutofRangeException" on the array "counts". I'm just trying to see if my threads are generally getting equal access to the class they're all trying to use at once.
public delegate void TempDel();
public TempDel InvokeTest;
public void TRTest3(Form1 sender)
{
InvokeTest = new TempDel(UpdateInvoke);
Thread t = new Thread(() => ConsoleUpdateTest(sender));
t.IsBackground = true;
t.Start();
POConsole.Instance.MaxLines = 20;
for(int i = 0; i < 100; i++)
{
Thread t2 = new Thread(() => ThreadWriting(i));
t2.IsBackground = true;
t2.Name = String.Format("Thread {0}", i);
t2.Start();
}
}
public ulong[] counts = new ulong[100];
public void ThreadWriting(int num)
{
if(num == 100)
{
bool stop = true;
}
while (true)
{
POConsole.Instance.WriteLine("Hello from Thread " + num);
counts[num]++;
}
}
public void ConsoleUpdateTest(Form1 sender)
{
while(true)
{
sender.Invoke(InvokeTest);
Thread.Sleep(5);
}
}
public void UpdateInvoke()
{
QuickTestBox.Text = POConsole.Instance.FullFeed;
}
All my threads are named, as you can see, and none of them receives the name "Thread 100" so I have no idea how one of the other threads could get passed a parameter of 100 or the parameter could be corrupted in some way.
Apparently my thread-safety checking isn't thread safe in some way?
This is a simple closure issue, you should not be using the for loop counter as a threading parameter issue, issue happens out here, for loop and thread execution do not run at same speed, so value of i can change for multiple threads:
for(int i = 0; i < 100; i++)
{
Thread t2 = new Thread(() => ThreadWriting(i));
t2.IsBackground = true;
t2.Name = String.Format("Thread {0}", i);
t2.Start();
}
Use following modification, create a local variable from loop counter
for(int i = 0; i < 100; i++)
{
int j = i;
Thread t2 = new Thread(() => ThreadWriting(j));
t2.IsBackground = true;
t2.Name = String.Format("Thread {0}", j);
t2.Start();
}