This piece of code causes an IndexOutOfBoundsException
Can anyone please tell me why?
I can't undertstand why it is causing an IndexOutOfBoundsException
private static String TRACE_PATH = "..\\..\\TRACES";
static void Main(string[] args)
{
if (Directory.Exists(TRACE_PATH))
{
String[] traceEntries = Directory.GetFiles(TRACE_PATH);
Thread[] traceReaders = new Thread[traceEntries.Length];
for (int i = 0; i < traceEntries.Length; i++)
{
traceReaders[i] = new Thread(()=>readTrace(traceEntries[i]));
traceReaders[i].Start();
}
}
Console.Read();
}
private static void readTrace(String traceFile)
{
using (StreamReader sr = new StreamReader(traceFile))
{
//code to use the trace file...
}
}
Just declare a temp variable inside your loop. You are capturing the variable not the value.
for (int i = 0; i < traceEntries.Length; i++)
{
var j = i;
traceReaders[j] = new Thread(()=>readTrace(traceEntries[j]));
traceReaders[j].Start();
}
I'd prefer to write it this way.
for (int i = 0; i < traceEntries.Length; i++)
{
var traceEntry = traceEntries[i];
traceReaders[i] = new Thread(() => readTrace(traceEntry));
traceReaders[i].Start();
}
The explanation is that your variable i is send as parameter to a lambda expression. When the lamba expression is executed on the thread, your for loop will be already done, and thus i will be equal to the traceEnties.Length.
By declaring the traceEntry as local variable, you remove the dependency of the local variable i which is updated in the for loop.
The same thing happens in L.B.'s answer. It's a matter of taste how to deal with it I guess.
Related
I've been learning about multi-thread programming and working on the dining philosophers problem. I'm trying to cause a deadlock without sleeping any threads. Here is the code snippet that I'm using:
public class Program
{
const int NumberOfPhilosophers = 5;
const int NumberOfForks = 5;
const int EatingTimeInMs = 20;
static object[] forks = new object[NumberOfForks];
static Thread[] philosopherEatingThreads = new Thread[NumberOfPhilosophers];
public static void Main(string[] args)
{
for (int i = 0; i < NumberOfForks; i++)
{
forks[i] = new object();
}
for (int i = 0; i < NumberOfPhilosophers; i++)
{
int philosopherIndex = i;
philosopherEatingThreads[i] = new Thread(() => { DoWork(philosopherIndex); })
{
Name = philosopherIndex.ToString()
};
philosopherEatingThreads[philosopherIndex].Start();
}
}
public static void DoWork(int philosopherIndex)
{
int fork1Index = philosopherIndex;
int fork2Index = (philosopherIndex + 1 ) % NumberOfForks;
var fork1 = forks[fork1Index];
var fork2 = forks[fork2Index];
lock (fork1)
{
lock (fork2)
{
Thread.Sleep(EatingTimeInMs);
}
}
}
}
I wasn't able to see any deadlocks after trying a couple of times. I know that not experiencing a deadlock does not mean that this code is thread-safe.
For example, when I change the lock statement and add latency I cause deadlock.
lock (fork1)
{
Thread.Sleep(10);
lock (fork2)
{
Thread.Sleep(EatingTimeInMs);
}
}
I have two questions:
Is using two lock statements one after another an atomic operation?
If using Thread.Sleep() causes a deadlock in a code snippet, does that mean that the code snippet is not thread-safe?
Thank you!
Once I run the following local, it is woking fast, but when I submit it to Kattis, It only exceeds 2/5 and I get Time Limit Exceeded.
Any suggestion?
I have tried with a input file with 10000 numbers and it is still fast localy :S
using System;
namespace phonelist
{
class Program
{
static void Main(string[] args)
{
int nrOfPhoneNrs = 0;
bool consistent;
int nrOfTestCases = Convert.ToInt32(Console.ReadLine().Trim());
for (byte i = 0; i < nrOfTestCases; i++)
{
consistent = false;
nrOfPhoneNrs = Convert.ToInt32(Console.ReadLine().Trim());
string[] phList = new string[nrOfPhoneNrs];
int n = 0;
while (n < nrOfPhoneNrs)
{
phList[n] = Console.ReadLine();
n++;
}
Array.Sort(phList);
int runs = nrOfPhoneNrs - 1;
for (int p = 0; p < runs; p++)
{
if (phList[p + 1].StartsWith(phList[p]))
{
consistent= true;
break;
}
}
Console.WriteLine(consistent? "NO" : "YES");
}
}
}
}
I think that your main problem is that you're using StartsWith and Array.Sort methods.
I don't want to give you too detailed advice (so that you can still solve it by yourself) but let me just suggest considering a different data structure than an array of strings, perhaps HashSet<string>.
I have to assign values to members(I don't know if this is the right term) of an array. I delcared my array members as follow: (all this code is inside a public class BWClass)
public static BackgroundWorker[] bwCA = new BackgroundWorker[25];
public static int NumbwCA;
private static bool HasRunOnce = false;
public static void BackgroundWorkerInitializer(bool doFirst, int numbwCA)
{
// Okay we got here. So we can presume that array checking (number not exceeding the array) is already done
if (!HasRunOnce)
{
NumbwCA = numbwCA; // for access
} // Now it is impossible to stop less backgroundworkers then started later on in the code
if (doFirst)
{
for (int i = 0; i < NumbwCA; i++)
{
string strpara = (numbwCA.ToString()); // Could also directly write numbwCA.ToString() directly in the RunWorkerAsync() method
bwCA = new BackgroundWorker[NumbwCA];
bwCA[i] = new BackgroundWorker();
bwCA[i].WorkerReportsProgress = true;
bwCA[i].WorkerSupportsCancellation = true;
bwCA[i].DoWork += new DoWorkEventHandler(bwa_DoWork);
bwCA[i].ProgressChanged += new ProgressChangedEventHandler(bwa_ProgressChanged);
bwCA[i].RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwa_RunWorkerCompleted);
bwCA[i].RunWorkerAsync(strpara);
}
}
else // DoSecond
{
// stop the backgroundworkers
for (int i = 0; i < NumbwCA; i++)
{
if (bwCA[i].IsBusy == true)
{
bwCA[i].CancelAsync();
HasRunOnce = false; // If restarting the server is required. The user won't have to restart the progrem then
}
else
{
Console.WriteLine(">>The backgroundworkers are already finished and don't need canceling");
}
}
}
}
Al this code is inside a public class.
I thought that when I do all the array making in a class I won't have the problem of variable scopes anymore. But I was wrong. I still get the Error nullReferenceException when the bwCA[i].IsBusy == true runs. Probably also when anything outside the for loop runs.
I know that I can't use the bwCA[i] outside the loop were it is declared but how do I change this, so that I can access bwCA[i] (anywhere) else in the code (like in the "else // DoSecond)?
btw. I prefer not to use a List
You need to move the array creation outside the loop
// here for instance
bwCA = new BackgroundWorker[NumbwCA];
if (doFirst)
{
for (int i = 0; i < NumbwCA; i++)
{
string strpara = (numbwCA.ToString());
// bwCA = new BackgroundWorker[NumbwCA];
That still leaves a few issues with cleanup and running this Initializer twice.
More general, try to avoid static stuff and you shouldn't be needing 25 Backgroundworkers in the first place.
Tasks are probably more appropriate but we can't tell.
i start 4 threads in a loop. each thread gets a reference to an array element to write the result.
But on the line where i create each thread, i get a System.IndexOutOfRangeException. I'm amazed that the index "i" is going out of range.
here is an example:
void ThreadsStarter()
{
double[] data = new double[4];
for(int i = 0; i < 4; i++)
{
Thread my_thread = new Thread(() => Work(data[i]));
my_thread.Start();
}
}
void Work(double data)
{
}
Why this is happening?
This is a common error: i gets evaluated when threads starts, which happens after the loop has ended. Make a temp, assign i to it, and use temp instead of i in your lambda to fix the issue:
void ThreadsStarter()
{
double[] data = new double[4];
for(int i = 0; i < 4; i++)
{
var temp = i;
Thread my_thread = new Thread(() => Work(ref data[temp]));
my_thread.Start();
}
}
void Work(ref double data)
{
}
I'm having a hard time in understanding the unexpected output for the following program:
class ThreadTest
{
static void Main()
{
for(int i = 0; i < 10; i++)
new Thread(() => Console.Write(i)).Start();
}
}
Queries:
Different code running in different thread have seperate stacks? If yes, than variables should preserve their values as int is a value type?
Each thread gets its own stack. The problem that you are facing has nothing to do with stack. The problem is the way it is generating code for your anonymous delegate. Use tool like refelector to understand the code that it is generating. The following will fix your problem:
static void Main()
{
for (int i = 0; i < 10; i++)
{
int capture = i;
new Thread(() => Console.Write(capture)).Start();
}
}
Under the hood
Whenever you use a variable from outer scope (in your case variable i) in anonymous delegate, the compiler generates a new class that wraps anonymous function along with the data that it uses from the outer scope. So in your case the generated class contains - one function and data member to capture the value of variable i. The class definition looks something like:
class SomeClass
{
public int i { get; set; }
public void Write()
{
Console.WriteLine(i);
}
}
The compiler re-writes your code as follows:
SomeClass someObj = new SomeClass();
for (int i = 0; i < 10; i++)
{
someObj.i = i;
new Thread(someObj.Write).Start();
}
and hence the problem - that you are facing. When you capture a variable, the compiler does the following:
for (int i = 0; i < 10; i++)
{
SomeClass someObj = new SomeClass();
someObj.i = i;
new Thread(someObj.Write).Start();
}
Note the difference in SomeClass instantiation. When you capture a variable, it creates as many instances as there are number of iterations. If you do not capture a variable, it tries to use the same instance for all iterations.
Hope, the above explanation will clarify your doubt.
Thanks
Yes, threads have their own stacks. But here you also have an issue with variable capture. Try changing the code to:
class ThreadTest
{
static void Main()
{
for(int i = 0; i < 10; i++)
{
int j = i;
new Thread(() => Console.Write(j)).Start();
}
}
}
Notice the change in output? Each thread is being started with a reference to the variable, not the value. When I insert the int j = i; line, we are breaking the variable capture. Your unexpected output has less to do with threading than it does closures.