Reputation: 1456
Assuming a static method like below is called from ASP.NET page,
can a different thread(b) overwrite the value of s1
after the first line is executed by thread(a)?
If so, can assigning parameters to local variables before manipulation solve this?
public static string TestMethod(string s1, string s2, string s3)
{
s1 = s2 + s3;
....
...
return s1;
}
Is there are a simple way to recreate such thread safety related issues?
Thanks.
Upvotes: 0
Views: 1687
Reputation: 135
I had same confusion too and here is my test code. Just sharing it ...
public partial class _default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
ThreadTest b = new ThreadTest();
Thread t = new Thread(new ParameterizedThreadStart(ThreadTest.sum));
Thread t1 = new Thread(new ParameterizedThreadStart(ThreadTest.sum));
t.Start(10);
t1.Start(12);
}
}
class ThreadTest
{
public static void sum(object XX)
{
int x = (int)XX;
for (int i = 0; i < x; i++)
{
System.Diagnostics.Debug.WriteLine("max : " + x + " --- " + i.ToString());
}
}
}
... Now if you run this you will see that int x is safe. so local non static variables are safe for a process and can not crippled by multiple thread
Upvotes: 1
Reputation: 19781
Yes, under some condition, as seen by the example code.
public static class ConsoleApp {
public static void Main() {
Console.WriteLine("Write something.");
var str = Console.ReadLine();
if (String.IsNullOrEmpty(str))
return;
new Thread(() => TestMethod(null, str, "")).Start();
// Allow TestMethod to execute.
Thread.Sleep(100);
unsafe {
// Grab pointer to our string.
var gcHandle = GCHandle.Alloc(str, GCHandleType.Pinned);
var strPtr = (char*)gcHandle.AddrOfPinnedObject().ToPointer();
// Change it, one character at a time, wait a little more than
// TestMethod for dramatic effect.
for (int i = 0; i < str.Length; ++i) {
strPtr[i] = 'x';
Thread.Sleep(1100);
}
}
// Tell TestMethod to quit.
_done = true;
Console.WriteLine("Done.");
Console.ReadLine();
}
private static Boolean _done;
public static void TestMethod(String x, String y, String z) {
x = y + z;
while (!_done) {
Console.WriteLine("{0} {1}", DateTime.Now.ToLongTimeString(), x);
Thread.Sleep(1000);
}
}
}
Requirements (afaik)
str0 == String.Empty
or str1 == String.Empty
, which returns the non-empty string. Concatenating three or more strings would create a new string which blocks this.Here's a fixed version of your modified one.
public static class ConsoleApp {
private static Int32 _counter = 10;
public static void Main() {
for (var i = 0; i < 10; i++) {
var str = GetString();
Console.WriteLine("Input: {0} - {1}", DateTime.Now.ToLongTimeString(), str);
new Thread(() => TestMethod(str)).Start();
unsafe {
var gcHandle = GCHandle.Alloc(str, GCHandleType.Pinned);
var strPtr = (char*)gcHandle.AddrOfPinnedObject().ToPointer();
strPtr[0] = 'A';
strPtr[1] = 'B';
strPtr[2] = 'C';
strPtr[3] = 'D';
strPtr[4] = 'E';
}
}
Console.WriteLine("Done.");
Console.ReadLine();
}
private static String GetString() {
var builder = new StringBuilder();
for (var i = _counter; i < _counter + 10; i++)
builder.Append(i.ToString());
_counter = _counter + 10;
return builder.ToString();
}
public static void TestMethod(Object y) {
Thread.Sleep(2000);
Console.WriteLine("Output: {0} {1}", DateTime.Now.ToLongTimeString(), y);
}
}
This still works because Object.ToString() is overriden in String to return this, thus returning the exact same reference.
Upvotes: 0
Reputation: 1456
Thanks Simon, here is the my evaluation on this. In the following code, i spawn threads simply using Thread.Start and the output becomes inconsistent.
This is proving that string passed on to a method can be modified.
If otherwise please explain!
public static class ConsoleApp{
[ThreadStatic]
private static int counter = 10;
public static void Main()
{
string str;
object obj = new object();
// Change it, one character at a time, wait a little more than
// TestMethod for dramatic effect.
for (int i = 0; i < 10; i++)
{
lock (obj)
{
str = GetString();
Console.WriteLine(DateTime.Now.ToLongTimeString());
//ThreadPool.QueueUserWorkItem(TestMethod, str);
new Thread(() => TestMethod(str)).Start();
}
}
Console.WriteLine("Done.");
Console.ReadLine();
}
private static string GetString()
{
object obj = new object();
lock (obj)
{
StringBuilder sb = new StringBuilder();
int temp = 0;
for (int i = counter; i < counter + 10; i++)
{
sb.Append(i.ToString());
temp = i;
}
counter = temp;
return sb.ToString();
}
}
public static void TestMethod(object y)
{
Thread.Sleep(2000);
Console.WriteLine("{0} {1}", DateTime.Now.ToLongTimeString(), y.ToString());
}
}
Thanks.
Upvotes: 0
Reputation: 1499790
No, the parameters are local variables - they're independent of any other threads. As strings are also immutable, you're safe. If these were mutable - e.g. a parameter of StringBuilder s1
- then although the value of s1
(a reference) couldn't be changed, the object that the parameter referred to could change its contents.
ref
and out
parameters could potentially have issues, as they can alias variables which are shared between threads.
Upvotes: 4