What does a function being thread-safe means with regards to its arguments?

huangapple go评论57阅读模式
英文:

What does a function being thread-safe means with regards to its arguments?

问题

我的当前对线程安全的理解是:

  • 如果一个函数是线程安全的,那么任何现有的函数(包括它自己)都可以在其他线程中并发运行,而不会发生意外的交互。
  • 如果一个类型是线程安全的,那么它不直接暴露其内部,并且构成其公共接口的函数是线程安全的。

基本上我理解为:

  • 逻辑必须考虑可能的其他线程
    • 线程安全的函数必须假设所有信息立即过时,除非它采取措施确保不是这样
    • 线程安全类型的接口应避免在用户检查它们并调用函数之间可能无效的先决条件
  • 在线程安全函数中访问任何成员/静态/全局变量必须是只读的(如果它是不可变的)或受保护的(例如,通过互斥锁)

我以为这样就包括了一切,但后来我遇到了一个形式为:

template <typename IteratorType>
void performSomeOperationOnARange(IteratorType const& itBegin, IteratorType const& itEnd);

这个函数不访问任何成员/静态/全局变量,所以我想认为它是线程安全的。
然而,在同一范围内并发运行它确实会导致意外的交互,所以我倾向于认为它是不安全的。
话虽如此,期望该函数保护对其一无所知的数据的访问似乎是不合理的。
将其推广为一个完整的原则,这意味着保护对函数参数的访问应该由调用者而不是被调用者负责。
然而,这似乎过于宽泛。this 是传递给每个非静态成员函数的参数(尽管是隐式的),在这种情况下确保对成员变量的访问是安全的明显是被调用者的责任。

因此,标题中的问题是:关于其参数,函数线程安全意味着什么? 在调用者和被调用者的责任之间如何划清界限?

英文:

My current understanding of thread safety is that:

  • A function is thread-safe if any existing functions (including itself) can be run concurrently in other threads without unintended interaction
  • A type is thread-safe if it doesn't directly expose its internals and if the functions making up its public interface are thread-safe

Which I basically took to mean that:

  • Logic must account for possible other threads
    • A thread-safe function must assume that all information is immediately stale unless it takes steps to ensure it is not
    • A thread-safe type's interface should avoid preconditions that could be invalidated between the user checking them and calling the function
  • Access to any member/static/global variable in a thread-safe function must either be read-only (if it's immutable) or be protected (e.g. by mutexes)

I thought that covered everything, but then I ran into a function of the form:

template &lt;typename IteratorType&gt;
void performSomeOperationOnARange(IteratorType const&amp; itBegin, IteratorType const&amp; itEnd);
  • This function doesn't access any member/static/global variable, so I want to consider it thread-safe
  • Running it concurrently on the same range definitely leads to unintended interaction though, so I'm tempted to consider it unsafe
  • That being said, it seems unreasonable to expect the function to protect access to data it knows nothing about
  • Extending that into a full principle, that would mean that protecting access to a function's arguments should fall to the caller, not the callee
  • This however feels too broad. this is an argument passed to every non-static member function (albeit implicitly), and in that case ensuring that access to member variables is safe definitely falls to the callee

Hence the question in the title: What does a function being thread-safe means with regards to its arguments? Where do you draw the line between the responsibility of the caller and the callee?

答案1

得分: 2

你对线程安全以及其对函数和类型的影响的理解通常是正确的。但是,当涉及到函数参数的线程安全性时,答案取决于函数在哪种上下文中使用。

一般来说,函数应该假设其参数是不线程安全的,除非明确文档化另有说明。这意味着,如果函数接受一个指针或引用,该指针或引用可能会被多个线程并发访问,那么确保对象受到适当保护或同步的责任应该由调用者来承担。

在您的特定示例中,函数performSomeOperationOnARange接受迭代器作为参数,迭代器通常是轻量级对象,不具备任何线程安全性保证。调用者有责任确保传递给函数的范围不会被其他线程并发访问。如果范围在多个线程之间共享,调用者应使用互斥锁或其他同步机制来同步对范围的访问。

另一方面,如果函数接受一个指向已经保证是线程安全的对象的指针或引用,比如不可变对象或线程安全的容器,那么该函数也可以被视为是线程安全的。

总的来说,确保线程安全的责任应该在调用者和被调用者之间明确文档化和传达,特别是如果该函数是公共接口或API的一部分。

英文:

Your understanding of thread safety and its implications on functions and types is generally correct. However, when it comes to the thread safety of function arguments, the answer depends on the context in which the function is being used.

In general, a function should assume that its arguments are not thread-safe unless explicitly documented otherwise. This means that if a function takes a pointer or reference to an object that may be accessed concurrently by multiple threads, it should be the responsibility of the caller to ensure that the object is protected or synchronized appropriately.

In your specific example, the function performSomeOperationOnARange takes iterators as arguments, which are typically lightweight objects that don't carry any thread-safety guarantees. It is up to the caller to ensure that the range being passed to the function is not being accessed concurrently by other threads. If the range is being shared between threads, the caller should synchronize access to the range using a mutex or other synchronization mechanism.

On the other hand, if a function takes a pointer or reference to an object that is guaranteed to be thread-safe, such as an immutable object or a thread-safe container, the function can be considered thread-safe as well.

In general, the responsibility for ensuring thread safety should be clearly documented and communicated between the caller and callee, especially if the function is part of a public interface or API.

huangapple
  • 本文由 发表于 2023年4月17日 16:55:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/76033353.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定