我有一个像这样的静态方法
public static string DoSomethingToString(string UntrustedString)
{
//parse format and change string here.
return newString.
}
因为我知道多次调用:
static int myInt=0;
public static int AddNumber()
{
lock(someObject)
{
myInt++;
return myInt;
}
}
将返回一个不断增加的数字(在页面之间共享),我不确定在DoSomethingToString()中如何处理变量;
我想了解一下静态方法在ASP.net中可以安全/不安全地使用的条件,只是为了让我的多线程大脑依赖于这个主题。
更新
大多数讨论围绕着价值类型。我怎么知道何时调用方法(显式或隐式)来改变我的引用类型是安全的?是否足以查看MSDN文档并仅使用其中显示“threadSafe”的方法?
如果我称之为隐式更改的一个例子是使用String.Split(),因为它是同一个类的一部分。我认为有可能会分享一些安全特性,并且需要减少担心/勤勉。 (?)
我所谓的显式更改(因为现在缺少更好的词)是调用另一种方法来做工作......可以是静态的或实例类。我认为需要做更多的背景/研究工作来确保每个对象和方法都是ThreadSafe。
为了便于讨论,假设我有一个带有此签名的方法:
ValidateStringAndContext(string untrustedString, Object myCustomUserContext)
它有一个引用以下对象的静态方法
public SecurityChecker
{
public static object CheckSecurityStatic(string DirtyData)
{
//do string.split
//maybe call a database, see if it's a token replay
//
//OR - alternate implementation
SecurityChecker sc = new SecurityChecker();
if (sc.CheckSecurity(DirtyData))
{
myCustomUserContext.Property1 = new GUID()
}
return myCustomUserContext;
}
public class bool CheckSecurity(string DirtyData)
{
//do string.split
//maybe call a database, see if it's a token replay
// return true if OK return false if not
}
}
修订问题
如果我创建的静态“实用程序”类是创建另一个对象的实例然后调用方法--versus--直接调用静态方法,我会遇到并发问题(变量会互相覆盖)吗? / p>
答案 0 :(得分:2)
调用静态方法的每个线程都有自己的调用堆栈。每次对DoSomethingToString()的调用都有自己的方法变量副本,完全独立于另一个线程上对该方法的任何其他调用(除非你将变量声明为静态,这就是你可能会混淆的东西 - 静态变量只有一个可供程序中多个线程访问的实例。
与往常一样,当多个线程访问共享资源时,您需要考虑并发性。当资源仅存在于方法调用的上下文中时,并发性不是问题。
另请参阅:https://stackoverflow.com/questions/511378/net-static-methods-and-its-effects-on-concurrencyt
答案 1 :(得分:1)
有一些非常重要的要点与代码中显示的代码有关... ...
DoSomeThingToString方法是静态的,并且在该方法中声明的任何变量都将是该线程的调用堆栈的本地变量。如果使用的变量是在函数外部定义的,那么您将在该内存上具有竞争条件。确保它看起来像这样,只使用局部变量:
public static string DoSomethingToString(string UntrustedString)
{
var newString = UntrustedString;
// operations on newString...
return newString;
}
AddNumber方法可能会受到其他可能不明显的问题的影响。如果这是你想要做的,添加一个数字,就这样做:
System.Threading.Interlocked.Increment(ref myInt);
Interlocked.Increment方法保证操作将在一个时钟周期内完成。
否则,在某些罕见的情况下使用lock关键字会很棘手。这是一些经验法则。始终锁定您创建并保持对其的引用的对象,甚至更好,您永远无法更改。这意味着:readonly,并在创建类时分配内存地址。看起来像这样:
static int myInt=0;
static readonly object aGoodLock = new object();
public static int MoreComplexIntStuff()
{
lock(aGoodLock)
{
// Do stuff with myInt...
}
return myInt;
}
此外,这不是全部。另一个问题是,无论何时访问变量myInt,即使它在该类的另一部分中 - 或者如果它是公共的并在其他地方使用,您需要用锁来包装它。而不仅仅是锁定,而是你使用的那个锁定,aGoodLock。
帮助你的开发人员(也许你自己的长期记忆)的最好方法是制作变量和包裹它的锁private
,并使用你想要的属性公开myInt小心在get和set中使用锁。
答案 2 :(得分:1)
我觉得你认为变量只能是实例或静态,并且忘记了局部变量也不同。考虑:
public class MyClass
{
public int InstanceInt;
public static int StaticInt;
public int InstanceMethod()
{
int i = new Random().Next(1, 50);
return i;
}
public static int StaticMethod()
{
int j = new Random().Next(1, 50);
return j;
}
}
此处InstanceInt
是实例变量,StaticInt
是静态变量。如果它们将被不同的线程访问,则这两个都需要锁定,但是,如果InstanceInt
的实例被不同的线程访问,MyClass
将仅由不同的线程访问。这可能发生在static MyClass AllThreadsSeeMe = new MyClass()
,存储在静态集合中,显式地将其传递给另一个线程,等等,否则就不会发生。 StaticInt
同时对应用程序中运行的所有线程都是固有可访问的,即使您注意确保线程之间不共享MyClass
的实例。
同时,i
和j
都是其功能的本地。调用该函数的每个线程都将获得自己的i
或j
副本,无论有多少线程可以调用它们。因此,它们本质上是线程安全的。只有当这些方法中的任何一个改变静态或实例变量,或者读取可由不同方法改变的可变静态或实例变量时,它们才是线程安全的(不可变的 - readonly
- 变量是还有线程安全,因为没有其他线程可以改变它们的值,因为根本没有线程可以改变它。)