Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/291.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/logging/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 这个扩展方法是线程安全的吗?_C# - Fatal编程技术网

C# 这个扩展方法是线程安全的吗?

C# 这个扩展方法是线程安全的吗?,c#,C#,我已经创建了在对象之间映射的扩展方法,但我担心它可能不是线程安全的。方法如下: public static SavableRecord ToSavableRecordForMongoDB(this Record record) { SavableRecord savableRecord = new SavableRecord(); if (record.Fields == null) throw new ArgumentException("Field

我已经创建了在对象之间映射的扩展方法,但我担心它可能不是线程安全的。方法如下:

public static SavableRecord ToSavableRecordForMongoDB(this Record record)
{
    SavableRecord savableRecord = new SavableRecord();

    if (record.Fields == null)
        throw new ArgumentException("Fields of record cannot be null");

    if (string.IsNullOrWhiteSpace(record.id))
        savableRecord._id = record.id;

    foreach (KeyValuePair<string, Field> item in record.Fields)
        savableRecord.Fields[item.Key] = new Field(item.Value);

    return savableRecord;
}
public static SavableRecord to savableRecordforMongoDB(此记录)
{
SavableRecord SavableRecord=新的SavableRecord();
if(record.Fields==null)
抛出新ArgumentException(“记录字段不能为空”);
if(string.IsNullOrWhiteSpace(record.id))
savableRecord.\u id=record.id;
foreach(record.Fields中的KeyValuePair项)
savableRecord.Fields[item.Key]=新字段(item.Value);
返回可保存记录;
}
如果这个方法不是线程安全的,我可以做什么使它成为线程安全的

更新
在MVC项目中,
记录
对象被传递到控制器中。
record
对象在此控制器或其路径中时不会更改。

这取决于
record
是否不可变

如果
记录
id
字段
仅在对象创建期间设置,则此方法是纯线程安全的-它不会产生任何副作用(假设
SavableRecord
没有),并且对于相同的输入总是返回相同的结果。您可以在十个线程上并行调用它,而不会出现任何冲突、数据损坏或混乱


但是,如果
字段
(或
id
)是可变的,则一个线程可以在执行此方法时更改值,从而导致意外结果,从引发异常的
字段
迭代器开始,在验证检查后属性被修改为无效值,直至丢失值,或者对该函数的两次调用返回不同的值。

否,一般来说,该方法就其参数
记录而言绝对不是线程安全的。你怎么知道?您正在(必然)不同的时间访问同一对象的多个属性,所有属性都不包含在一个同步部分中(例如
lock
语句)。因此,任何其他线程都可能同时更改对象,使其与方法的逻辑预期不一致

最严重的例子是一个
null
检查
record.Fields
,然后用相同的方法在循环中访问相同的属性。如果任何其他线程同时将
null
分配给
record.Fields
,则您的代码必然会导致
NullReferenceException
。此外,这种行为很可能看起来完全是随机的

但是,如果对象是不可变的,那么情况就不同了,您的代码将是线程安全的

如果这个方法不是线程安全的,我可以做什么使它成为线程安全的

使用lock语句:

public static SavableRecord ToSavableRecordForMongoDB(this Record record)
{
    lock (lockObject)
    {
        … the logic of your method …
    }
}

lockObject
假设什么在很大程度上取决于代码的总体逻辑。当然,所有需要对同一
记录
实例进行写访问的线程都需要在同一
锁对象
上同步,以实现线程安全行为。有关各种类型的线程同步的更多信息,请参见,例如,猜测适用于实例方法的相同原则线程安全也适用于扩展方法。在上述情况下,访问ToSavableRecordForMongoDB方法的每个线程将获得声明该方法的类的新实例。在这个方法中,我没有看到任何静态字段被修改。在我看来线程是安全的

这一切都取决于其他线程是否将使用传递给此扩展方法的
记录
。我明白了,因此如果其他线程更改记录对象的值,那么这些值可能会在此扩展方法中更改。其他线程将不使用记录对象,所以我应该是好的?理论上,是的。但同样,这一切都取决于实施。检查您的代码路径,确保不会出现争用情况。顺便说一句,
ArgumentException
应该是一个
ArgumentNullException
。不可变的-很好的观点。更新了我自己的答案,参考了你的s.Hi@Avner Shahar Kashtan,问题。类扩展ToSaveRecordFormOnGoDB是静态的。多个线程不能同时访问/使用此静态方法吗?这是否意味着两个线程将同时尝试修改记录,因此存在争用条件?@Fractal您可以有5个线程或50个线程。只要每个线程只读取记录并创建不同的savablecord,它们就可以安全共存。好的,非常感谢@Avner Shahar Kashtan的澄清。有道理。在这里使用
锁实际上没有帮助,除非您可以确保修改记录的任何其他代码也检查相同的锁。这不会阻止其他方法修改项目的属性。@Avnershaar Kashtan我暗示过。但总的来说,这是一个很好的改进点和一个明确的提及。