5

Linux で pthread ライブラリを使用しています。

スレッド A で文字列を割り当て、スレッド B で文字列を出力しようとしています。ただし、文字列は空で出力されます (スレッド A で動作することを確認しました)。

注:文字列はオブジェクト内に存在します。これは、クリーンアップされているか、空で再インスタンス化されている可能性があります...コンテナオブジェクトは、セグフォルトなどを与えません。すべての値が空です。

これは、スレッドが他のスレッドからメモリにアクセスできないためですか、またはスレッド A が停止するとメモリが割り当てられていないためですか? それともどちらでもありません。私のコードのバグかもしれませんが、これを除外したかっただけです...

アップデート:

メモリの問題であることが判明しました。あなたの答えのおかげで、私もこれに自分で答えました。同意/同意しない場合は、私の答えにコメントしてください。

4

8 に答える 8

9

スレッドは、プロセスとは異なり、プロセス内で共通のメモリ空間を共有します (各スレッドには独自のスタックがありますが、ヒープは通常共有されます)。したがって、スレッドを終了しても、共有ヒープから割り当てられたメモリは自動的に解放されません。ただし、たとえば、スタックに文字列オブジェクトを割り当て、単純なポインターでどこかに渡した場合、スレッドが終了すると、デストラクタによってメモリが解放されます。

于 2009-04-07T07:58:18.303 に答える
2

各スレッドには「独自の」メモリ (つまり、独自のヒープ スペース) がありますが、メモリはメモリです。スレッドを開始しても、別のスレッドのメモリには影響しません。スレッド B はスレッド A の文字列へのポインタをどのように取得しますか?

詳細を説明する必要があります...文字列をヒープに置いていますか? ヒープ上にない場合、スレッド A が終了するとおそらく消えます...そうでなければ、バグがあります...もっと投稿してください!

于 2009-04-07T07:59:09.203 に答える
1

「正しい方法」は機能しますが、元の問題を修正するにはあまりにも多くの方法があります。

以下は、元の問題に必要な唯一の変更です。

void onlyFunctionRunFromThread2()
{
    MyType1 &mt1 = myMap[0];

   ...

あなたの「正しい方法」で、あなたが新しいもので割り当てたメモリを解放する責任があるのはあなたです。あなたの元の例では、それは「自動的に」(いわば)行われました。

また、どちらのソリューションにも、マップまたはマップのアイテムのロックがありません。これは、まったく別の質問です。

于 2009-04-08T17:37:17.387 に答える
1

はい、メモリは割り当てられたままです。

文字列を含むクラスの dtor にブレーク ポイントまたは何らかのログを配置し、何が起こっているかを確認します。

お役に立てれば

于 2009-04-07T07:59:55.023 に答える
0

スレッドが終了すると、そのスタックの割り当てが解除されます。ただし、ヒープ メモリはスレッド固有ではないため、クリーンアップを実行しない限り、ヒープの割り当てはそこに残ります。

文字列はスタックに割り当てられていますか? その場合、同じコードが 2 つのスレッドで実行されていても、スレッド間で文字列を共有していません。

次の例では、関数 foo が 2 つのスレッドによって実行されます。

void foo( void*)
{
   std::string myString;
   // Do something with your string....
}

myString はスレッド間で共有されません。各スレッドは、独自のスタックに文字列オブジェクトを持っています。

それで、あなたが言及している実際の状況は何ですか?

于 2009-04-07T07:59:05.057 に答える
0

共有リソース (つまり文字列) にアクセスするときにロックを使用しますか?

于 2009-04-07T07:59:25.773 に答える
0

予想通り、この問題はメモリの不適切な使用が原因であることが判明しました。次の例が正しいと 99% 確信しています。それはほとんど疑似コードなので、明らかにコンパイルされません。

アップデート:

nusi のおかげで 3 番目のソリューションを追加しました。

間違った方法 (スタック メモリを使用):

std::map<int, MyType1> myMap;

void firstFunctionRunFromThread1()
{
    MyType1 mt1;
    mt1.Test = "Test 1";
    myMap[0] = mt1;
}

void onlyFunctionRunFromThread2()
{
    MyType1 mt1 = myMap[0];

    // This actually does print "Test 1", so the memory is being accessed.
    std::cout << mt1.Test << endl;

    /* However, because we're using stack memory here, this value is lost
     * when we go back to thread #1. */
    mt1.Test = "Test 2";
}

void secondFunctionFromThread1()
{
    MyType1 mt1 = myMap[0];

    // This will actually print out "Test 1", where "Test 2" is expected!
    std::cout << mt1.Test << endl;
}

複雑で正しい方法 (ヒープメモリを使用) :

スタック メモリを使用する簡単な方法も参照してください。

std::map<int, MyType1> myMap;

void firstFunctionRunFromThread1()
{
    // Use heap memory so the memory stays allocated.
    MyType1 *mt1 = new MyType1();
    mt1->Test = "Test 1";
    myMap[0] = *mt1;
}

void onlyFunctionRunFromThread2()
{
    /* Now, get a pointer to the object; we can't use stack memory
     * here because the values assigned would be lost as soon as 
     * we try and access them from secondFunctionFromThread1() */
    MyType1 *mt1 = &myMap[0];

    // As expected, this prints "Test 1"
    std::cout << mt1->Test << endl;

    /* Now, because we're assigning to memory on the heap, this will
     * stay assigned until the entire application exits, yay! */
    mt1->Test = "Test 2";
}

void secondFunctionFromThread1()
{
    /* Not sure if using heap memory here is neccecary, but we'll do
     * it anwyay just to play on the safe side... let me know if this
     * is pointless... */
    MyType1 *mt1 = &myMap[0];

    // Hurray, this prints "Test 2"!!! :)
    std::cout << mt1->Test << endl;
}

シンプルで正しい方法 (スタック メモリを正しく使用) :

答えてくれたnusiに感謝します。

std::map<int, MyType1> myMap;

void firstFunctionRunFromThread1()
{
    MyType1 mt1;
    mt1.Test = "Test 1";
    myMap[0] = mt1;
}

void onlyFunctionRunFromThread2()
{
    /* Using the & before the variable turns it into a reference, so
     * instead of using stack memory, we use the original memory.
     * NOTE: Is this explanation correct? */
    MyType1 &mt1 = myMap[0];

    // This actually does print "Test 1", so the memory is being accessed.
    std::cout << mt1.Test << endl;

    // We're assigning to the reference, so this works.
    mt1.Test = "Test 2";
}

void secondFunctionFromThread1()
{
    MyType1 mt1 = myMap[0];

    // Prints "Test 1" as expected.
    std::cout << mt1.Test << endl;
}
于 2009-04-08T16:51:26.187 に答える
0

すでに述べたように、スレッドはメモリに対して何もしません。プライベート メモリ空間はありません。これらは、2 つ以上の関数の「並列」実行を実装するためのオペレーティング システムのスケジューラへの単なるコマンドです。同じ変数にアクセスできる任意の 2 つの関数は、それらがスレッド化されている (並べて実行される) かどうか (順次実行される) に関係なく、そうすることができます。

多くの場合、スレッド化は恐ろしいモンスターのように見えますが、実際にはスケジューラーに追加の実行ポインターを追加するためのヒントを与えるだけです。

説明した問題をデバッグするには、int など、2 つの関数でより基本的な変数の型を変更するだけです。文字列などを変更するとすぐに、コンテナーがデータの内部コピーを行う可能性が非常に高くなります。

    int i; // 両方の関数のスコープ内の変数

ボイド f1()
{
   while(真)
    {
       i++;
       私は %= 128000;
    }
}
ボイド f2()
{
     while(真)
            std::cout
于 2009-04-07T14:04:44.787 に答える