2015-05-08 2 views
17

Можно ли поточно построить строки из локальных переменных, используя класс String, как в методах ниже? Предположим, что приведенные ниже методы вызывают из нескольких потоков.Безопасность потоков в классе String

public static string WriteResult(int value, string name) 
{ 
    return string.Format("Result: value={0} name={1}", value, name); 
} 

public static string WriteResult2(int value, string name) 
{ 
    return "Result: value=" + value + " name=" + name; 
} 

Или мне нужно использовать StringBuilder для обеспечения безопасности потоков?

ответ

19

Это абсолютно хорошо. Не существует общего состояния ни в одном фрагменте кода, кроме строковых литералов. Поскольку строки являются неизменяемыми, для строк можно свободно делиться между потоками, и оба string.Format и string.Concat (неявно называемые во втором фрагменте кода) являются потокобезопасными.

Даже если один из параметров был изменен, и даже если метод мутировал параметры, например.

public static void AddResult(int value, List<string> results) 
{ 
    results.Add("Value " + value); 
} 

... то сам метод все равно будет потокобезопасной, так долго, как несколько потоков не относятся к тому же List<string>. Если несколько потоков сделали, обратитесь к тому же List<string>, тогда он был бы небезопасным, даже если он просто читал из списка, так как другой поток мог бы его мутировать.

17

Оба параметра int и string как параметры этого метода являются фактически неизменяемыми и не могут быть изменены внешним кодом.

Таким образом, нет необходимости заботиться о безопасности нитей с помощью метода Format или String.Concat в этом случае.


Но давайте предположим, что мы имеем некоторый класс MyObject, что изменчиво и может быть изменен из-за пределов:

public class MyClass 
{ 
    public Int32 value1 { get; set; } 
    public String value2 { get; set;} 
} 

public static string WriteResult2(MyObject obj) 
{ 
    return "Result: value=" + obj.value1 + " name=" + obj.value2 ; 
} 

В этом случае, можно ли с первого захода на посадку или второй возврате противоречивое значение (это как value1, так и value2 изменяются после того, как одно значение уже отправлено на вывод.)

Как @JonSkeet pointed, это не сам метод, который является небезопасным, но сам класс небезопасен для совместного использования между различными потоками ,

Чтобы справиться с этой ситуацией, вам придется либо создать специальный поточно-метод экземпляра:

public class MyClass 
{ 
    private Object lockObj = new Object(); 
    public Int32 value1 
    { 
     get 
     { 
      lock (this.lockObj) { ... }); 
     } 
     set 
     { 
      lock (this.lockObj) { ... }); 
     } 
    } 
    public String value2 
    { 
     get 
     { 
      lock (this.lockObj) { ... }); 
     } 
     set 
     { 
      lock (this.lockObj) { ... }); 
     } 
    } 

    public string WriteResult2() 
    { 
     lock (this.lockObj) 
     { 
      return "Result: value=" + this.value1 + " name=" + this.value2 ; 
     } 
    } 
} 

Или использовать некоторые дополнительные блокировки над такими экземплярами в методах, которые используют его. Первый подход в классе, очевидно, менее подвержен ошибкам, но может снизить производительность и создать много кода котельной. В идеальном случае при параллельном программировании вам меньше нужно заботиться об общем изменчивом состоянии и его последовательности the better.

+7

Я бы не сказал, что ваш последний случай делает * метод * thread-unsafe - он небезопасен для * экземпляров * (MyClass) для обмена экземплярами между несколькими потоками, что немного отличается от IMO. –

+1

Ваша «поточно-безопасная» версия на самом деле не является потокобезопасной ... потому что любой другой поток может изменять значения «value1» и «value2», не приобретая вообще никакой блокировки. –

+1

@JonSkeet спасибо. Забыл добавить замки. –

5

Оба метода являются потокобезопасными, потому что неважно, что вы делаете внутри WriteResult. Пока он не использует изменчивое статическое состояние, и его параметры не могут быть изменены извне, ваш статический метод является потокобезопасным.

Легко проверить, что методы не используют статическое состояние.Это также легко проверить параметры этого метода не может быть изменен из-за пределов:

  • value не может быть изменен, потому что это примитивный тип, и он передается по значению
  • name не может быть изменен, так как string объектов являются неизменными.
Смежные вопросы