草庐IT

java - 托管语言是否锁定刷新并重新加载 native 库的变量?

coder 2023-11-17 原文

当我们在 C#Java 等托管语言中使用锁时,我们始终可以确保我们处理的是最新数据。

特别是在 Java 内存模型中,它们有一种称为先发生关系 的保证。 但我不确定原生库会发生什么情况。

比如说,我有这样的 C 函数:

static int sharedData;      // I'm not declaring this as volatile on purpose here.

void setData(int data) {
    sharedData = data;      // Not using any mutex or the like.
}

int getData() {
    return sharedData;
}

我也有像这样的 C# 代码:

// Thread 1
while( true )
    lock( key )
        setData( ++i );     // Calling a native C function using P/Invoke.

// Thread 2
while( true )
    lock( key )
        DoSomeJob( getData() );

如您所见,如果 C 端的 sharedData 没有声明为 volatile,那么是否仍然可以保证 Thread 2总能得到线程1设置的最新值吗?

同样适用于使用 JNIJava 吗?

最佳答案

As you see, if sharedData from C side is not declared as volatile, then is there still a guarantee that Thread 2 can always get the latest value set by Thread 1?

不,将其标记为 volatile 对任何一种线程都没有影响。

Does the same apply to Java using JNI too?

是的,它也适用于 PHP、Lua、Python 和任何其他可以以这种方式引入 C 库的语言。

详细说明您的第一个问题,C 中的 volatile 关键字不用于线程,它用于告诉编译器不要优化该变量。

以下面的代码为例:

#include <stdio.h>
#include <stdbool.h>
#include <limits.h>

static bool run; // = false

void do_run(void)
{
    unsigned long v = 1;
    while (run) {
        if (++v == ULONG_MAX) run = false;
    }
    printf("v = %lu\n", v);
}

void set_run(bool value)
{
    run = value;
}

int main(int argc, char** argv)
{
    set_run(true);
    do_run();
    return 0;
}

启用优化后,编译器可能会看到很多区域以无副作用地删除不必要的代码,然后这样做;例如,编译器可以在 do_run 函数中看到 unsigned long v 将始终是 ULONG_MAX 并选择简单地返回 ULONG_MAX

事实上,当我在上面的代码上运行 gcc -O3 时,这正是发生的事情,do_run 函数立即返回并打印 v = 18446744073709551615

如果您将run 标记为volatile,那么编译器将无法优化该变量,这通常意味着它无法优化具有该变量的代码区域某些方式。

也就是说,当我将 run 更改为 static volatile bool run; 然后使用 gcc -O3 进行编译时,我的程序现在停止等待让循环迭代 18446744073709551615 次。


除此之外,当您调用外部库时,您拥有的唯一线程安全是由该库中使用的语言提供的。

对于 C,您必须在函数中明确指定线程安全。因此,对于您的代码,即使您在托管代码中使用了 lock 上下文,它对托管代码进行锁定,并且 C 代码本身仍然不是线程安全的.

以下面的代码为例:

C代码

static volatile int sharedData;
static volatile bool doRun;
static pthread_t addThread;

void* runThread(void* data)
{
    while (doRun) {
        ++sharedData;
    }
    return NULL;
}

void startThread(void)
{
    doRun = true;
    pthread_create(&addThread, NULL, &runThread, NULL);
}

void stopThread(void)
{
    doRun = false;
}

void setData(int data)
{
    sharedData = data;
}

int getData(void)
{
    return sharedData;
}

C#代码

// Thread 1
startThread();
while (true) {
    lock (key) {
        setData(++i);
    }
}

// Thread 2
while (true) {
    lock (key) {
        i = getData();
    }
}
stopThread();

在这段代码中,当 lock (key) 被调用时,您唯一的保证是 i 将在 C# 代码中受到保护。但是,由于 C 代码也在运行一个线程(因为线程 1 调用了 startThread),那么您无法保证 C#代码将被正确同步。

要使 C 代码线程安全,您必须专门添加互斥量或信号量以满足您的需要:

static int sharedData;
static volatile bool doRun;
static pthread_t addThread;
static pthread_mutex_t key;

void* runThread(void* data)
{
    while (doRun) {
        pthread_mutex_lock(&key);
        ++sharedData;
        pthread_mutex_unlock(&key);
    }
    return NULL;
}

void startThread(void)
{
    doRun = true;
    pthread_mutex_init(&key, NULL);
    pthread_create(&addThread, NULL, &runThread, NULL);
}

void stopThread(void)
{
    doRun = false;
    pthread_mutex_lock(&key);
    pthread_mutex_unlock(&key);
    pthread_mutex_destroy(&key);
}

void setData(int data)
{
    pthread_mutex_lock(&key);
    sharedData = data;
    pthread_mutex_unlock(&key);
}

int getData(void)
{
    int ret = 0;
    pthread_mutex_lock(&key);
    ret = sharedData;
    pthread_mutex_unlock(&key);
    return ret;
}

通过这种方式,底层库调用得到了适当的保护,并且共享该库内存的任意数量的进程也将是线程安全的。

我应该注意,上面使用 POSIX 进行线程同步,但也可以使用 WinAPI 或 C11 标准互斥锁,具体取决于您的目标系统。

希望对您有所帮助。

关于java - 托管语言是否锁定刷新并重新加载 native 库的变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56787106/

有关java - 托管语言是否锁定刷新并重新加载 native 库的变量?的更多相关文章

  1. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  2. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  3. ruby-on-rails - 如何使用 instance_variable_set 正确设置实例变量? - 2

    我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击

  4. ruby - 检查数组是否在增加 - 2

    这个问题在这里已经有了答案:Checktoseeifanarrayisalreadysorted?(8个答案)关闭9年前。我只是想知道是否有办法检查数组是否在增加?这是我的解决方案,但我正在寻找更漂亮的方法:n=-1@arr.flatten.each{|e|returnfalseife

  5. ruby - 通过 ruby​​ 进程共享变量 - 2

    我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是

  6. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  7. ruby-on-rails - 如何在我的 Rails 应用程序 View 中打印 ruby​​ 变量的内容? - 2

    我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby​​中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R

  8. ruby - 检查字符串是否包含散列中的任何键并返回它包含的键的值 - 2

    我有一个包含多个键的散列和一个字符串,该字符串不包含散列中的任何键或包含一个键。h={"k1"=>"v1","k2"=>"v2","k3"=>"v3"}s="thisisanexamplestringthatmightoccurwithakeysomewhereinthestringk1(withspecialcharacterslike(^&*$#@!^&&*))"检查s是否包含h中的任何键的最佳方法是什么,如果包含,则返回它包含的键的值?例如,对于上面的h和s的例子,输出应该是v1。编辑:只有字符串是用户定义的。哈希将始终相同。 最佳答案

  9. ruby-on-rails - Ruby 检查日期时间是否为 iso8601 并保存 - 2

    我需要检查DateTime是否采用有效的ISO8601格式。喜欢:#iso8601?我检查了ruby​​是否有特定方法,但没有找到。目前我正在使用date.iso8601==date来检查这个。有什么好的方法吗?编辑解释我的环境,并改变问题的范围。因此,我的项目将使用jsapiFullCalendar,这就是我需要iso8601字符串格式的原因。我想知道更好或正确的方法是什么,以正确的格式将日期保存在数据库中,或者让ActiveRecord完成它们的工作并在我需要时间信息时对其进行操作。 最佳答案 我不太明白你的问题。我假设您想检查

  10. ruby - 检查日期是否在过去 7 天内 - 2

    我的日期格式如下:"%d-%m-%Y"(例如,今天的日期为07-09-2015),我想看看是不是在过去的七天内。谁能推荐一种方法? 最佳答案 你可以这样做:require"date"Date.today-7 关于ruby-检查日期是否在过去7天内,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/32438063/

随机推荐