2011-01-10 13 views
0

Angenommen, eine statische Methode wie unten wird von der ASP.NET-Seite aufgerufen, kann einen anderen Thread (b) den Wert s1 überschreiben, nachdem die erste Zeile von Thread (a) ausgeführt wurde ?Threadsicherheit in Parametern, die an eine statische Methode übergeben wurden

Wenn ja, kann die Zuweisung von Parametern zu lokalen Variablen vor der Manipulation dies lösen?

public static string TestMethod(string s1, string s2, string s3) 
{ 
    s1 = s2 + s3; 
    .... 
    ... 
    return s1; 
} 

Gibt es eine einfache Möglichkeit, um solche Probleme mit der Threadsicherheit wieder herzustellen?

Danke.

Antwort

0

Ja, unter bestimmten Bedingungen, wie aus dem Beispielcode ersichtlich.

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); 
     } 
    } 
} 

Anforderungen (afaik)

  1. Unsafe Kontext Zeiger zu verwenden.
  2. Verwenden Sie String.Concat (String str0, String str1), das für Fälle optimiert ist, in denen str0 == String.Empty oder str1 == String.Empty die nicht leere Zeichenfolge zurückgibt. Das Verketten von drei oder mehr Zeichenfolgen würde eine neue Zeichenfolge erzeugen, die dies blockiert.

Hier ist eine feste Version der eine modifizierte.

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); 
    } 
} 

noch Das funktioniert, weil Object.ToString() in String außer Kraft gesetzt wird diese zurück, so dass genau die gleiche Referenz zurück.

4

Nein, die Parameter sind lokale Variablen - sie sind unabhängig von anderen Threads. Da Strings auch unveränderlich sind, bist du in Sicherheit. Wenn diese veränderbar wären - z.B. Ein Parameter von StringBuilder s1 - obwohl der Werts1 (eine Referenz) nicht geändert werden konnte, konnte das Objekt, auf das der Parameter Bezug nahm, seinen Inhalt ändern.

ref und out Parameter möglicherweise Probleme haben, da sie Alias-Variablen, die zwischen Threads geteilt werden können.

+0

Danke. Parameter sind lokale Variablen und unabhängig von anderen Threads. Aber gibt es eine einfache Möglichkeit, solche Probleme neu zu erstellen/zu debuggen? – humblelistener

+0

@Pkay: Normalerweise nicht, fürchte ich. Threading-Bugs sind schwierig zu finden und zu testen :( –

0

Danke Simon, hier ist meine Bewertung dazu. Im folgenden Code spawne ich Threads einfach mit Thread.Start und die Ausgabe wird inkonsistent.

Dies beweist, dass an eine Methode übergebene Zeichenfolge geändert werden kann.

Wenn nicht anders bitte erklären!

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()); 
    } 
} 

Danke.

+0

Der Code, den ich eingefügt habe bewiesen, dass eine übergebene Zeichenfolge mit Zeigern geändert werden kann. Ihre Änderungen führen eine Menge neuer Kuriositäten, wie das Sperren von lokalen Objekten und Schließung Probleme beim Capturen von str zu übergeben Sie haben den Code, den ich geschrieben habe, vollständig gelöscht, der die Zeichenfolge tatsächlich geändert hat. – sisve

+0

Das ist richtig Simon, ich habe diesen Code tatsächlich von Ihrem abgeleitet. Das ganze Problem ist, dass die Ausgabe von dem obigen Code nicht wie erwartet ist etwas Licht darauf? – humblelistener

+0

Was sind Ihre Erwartungen? 1) Entfernen Sie alle Schlösser, sie haben keine Wirkung. 2) Deklariere str in deiner for-Schleife. Dies behebt ein Schließungsproblem, bei dem dieselbe Zeichenfolge mehrmals verwendet wird. 3) Fügen Sie den unsicheren Codeblock in Ihre for-Schleife ein, der die bereits übergebene Zeichenfolge ändert. Ich habe diese drei Schritte gemacht und das Ergebnis zu meiner Antwort hinzugefügt. – sisve

1

Ich hatte auch die gleiche Verwirrung und hier ist mein Testcode. Teilen Sie es einfach ...

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()); 
     } 
    } 
} 

... Nun, wenn Sie das ausführen, werden Sie sehen, dass int x sicher ist.So sind lokale nicht statische Variablen sicher für einen Prozess und können nicht durch mehrere Threads verkrüppelt werden.

Verwandte Themen