スレッドに割り当てるメソッドの違いで_メモリの使用に違いがあるのか?

 

func01は void の関数になっているが、

戻り値があるタイプにして、

特定の処理の時、

想定しているのは 制限時間が来たときの

カートの清算だが、

制限時間が来たときに、戻り値を

返して、呼び出し元から func02を実行する、

これだと、func01からfunc02への

移行を管理する関数が要るので、

あまり、かわらないのか



--------------------------------------------


サンプル01


class res_instance01 :


    def __init__( self ):

        

        self.state_event = threading.Event() 


        self.list_error_zero_division = []


 

    def is_state_changed( self ) :

        

        return self.state_event.is_set()



    def notify_state_change( self ) :

              

        self.state_event.set()



class task :

      

    def __init__( self, res_instance ):

        

        self.res_instance = res_instance


        self.list_error_zero_division = []

       

        self.bool_is_state_change = False



    def func01( self ) :

        

        #処理内容を記述

        

        self.bool_is_state_change = True

        

        while self.bool_is_state_change :

            

            

            # 1から10までの整数をランダムに取得

            num = random.randint(1, 4)


            if num = 3 :

                      

                bool_is_state_change = False

    

                break

    

        self.res_instance.func02()

'''

このあたりの部分で、3の値でループを終了し、

func01内部から func02を実行している

-> 詳しくは知らないが、スタックに積みあがる

func01は void の関数になっているが、

戻り値があるタイプにして、

特定の処理の時、

想定しているのは 制限時間が来たときの

カートの清算だが、

制限時間が来たときに、戻り値を

返して、呼び出し元から func02を実行する、

これだと、func01からfunc02への

移行を管理する関数が要るので、

あまり、かわらないのか

''

                     

    def func02( self ) : 


        # 処理内容を記述

                

        pass

        

        

class Main :


    def __init__( self ):

        


self.thread01 = None

        

        self.task01 = task( self.res_instance )

def main01( self ) :

        self.run_thread01()



def run_thread01( self ) :



self.thread01 = threading.Thread(

        

            target=self.task01.func01,

                      

            )


        self.thread01.start()



if __name__ == "__main__":


    

    app = Main()


    app.main01()



サンプル01のおわり




サンプル02


class res_instance01 :


    def __init__( self ):

        

        self.state_event = threading.Event() 


        self.list_error_zero_division = []


 

    def is_state_changed( self ) :

        

        return self.state_event.is_set()



    def notify_state_change( self ) :

              

        self.state_event.set()



class task :

      

    def __init__( self, res_instance ):

        

        self.res_instance = res_instance


        self.list_error_zero_division = []

       

        self.bool_is_state_change = False



    def func01( self ) :

        

        #処理内容を記述

        

        self.bool_is_state_change = True

        

        while self.bool_is_state_change :

                       

            # 1から10までの整数をランダムに取得

            num = random.randint(1, 4)


            if num = 3 :

                

                self.res_instance.notify_state_change() 

                

                bool_is_state_change = False

    


               

    def func02( self ) : 


        # 処理内容を記述

                

        pass

        

        

               

class Main :



    def __init__( self ):


       

self.thread01 = None

self.thread02 = None

        

        self.monitoring_thread = None


        self.res_instance = res_instance()

        

        self.task01 = task( self.res_instance )


def main01( self ) :

       

        self.run_monitoring_thread()       

        self.run_thread01()



    def run_monitoring_thread( self ) :

        

        self.monitoring_thread = threading.Thread(

        

            target=self.state_monitoring,

            

            

            )


        self.monitoring_thread.start()  




    def state_monitoring( self ) : 

 

        while self.bool_is_monitoring :

            

            if self.res_instance.is_state_changed()

            

                self.thread02 = threading.Thread(

                     

                    target=self.task01.func01,

                   

                )


                self.thread02.start()            

                self.bool_is_monitoring = False




def run_thread01( self ) :



self.thread01 = threading.Thread(

        

            target=self.task01.func01,

                      

            )


        self.thread01.start()



def run_thread02( self ) :


self.thread02 = threading.Thread(

        

           target=self.task01.func02,

                      

           )


        self.thread02.start()



if __name__ == "__main__":


    

    app = Main()


    app.main01()



サンプル02のおわり


上記の2つのコードにはres_instanceのクラスの使い方などに

違いがありますが、


taskクラスでの一連の操作に区切りがあれば、

スレッドを分けたほうがいいのでしょうか?


メモリの使い方に差があるのでしょうか?


インスタンスメソッド内で、

インスタンスメソッドをコールすると、


前のメソッドのスコープがメモリに割り当てられた状態のまま

次のメソッドがスタックに積みあがる、


ということを chatGPTさんに教わったことがあるのですが・・・






### スレッドを分けるべきか?


スレッドを分けるかどうかは 

**処理の独立性** によります。


#### ① シングルスレッド(スレッドを分けない)


もし、`task` クラスの一連の処理が

「順番に実行されるべきもの」であれば、

スレッドを分ける必要はありません。


例えば、`func01()` の処理が終わってから 

`func02()` を呼ぶなら、

同じスレッド内で実行するほうがシンプルです。



#### ② マルチスレッド(スレッドを分ける)


一方で、`func01()` の処理が終わるのを待たずに 

`func02()` を実行したいなら、

スレッドを分けることで並列処理が可能になります。


例えば、`func01()` が 

`notify_state_change()` を呼び出し、

それを監視する `state_monitoring()` が 

`func02()` を別スレッドで実行するのは、

非同期処理を意図した設計になっています。


サンプル02では、`func01()` で 

`notify_state_change()` を呼び出し、

`state_monitoring()` が

新しいスレッド (`thread02`) で 

`func02()` を呼び出すようになっており、

処理が並列化されています。


---


### メモリの使い方の違い


スレッドを分けた場合、

各スレッドは**独立したスタック**を持つため、

それぞれのスレッドが個別にメソッドを呼び出しても、

他のスレッドのスタックには影響を与えません。


ただし、スレッドを分けるとメモリ使用量が増える可能性があります。


- **スレッドを分けると**、

それぞれのスレッドが**独立したスタックを持つ**ため、

複数のスレッドを作ると**

スレッドごとのスタックメモリ**が消費されます。


- 一方、シングルスレッドなら、

**1つのスタックだけ**が使われるため、

メモリ使用量は抑えられます。


また、`res_instance` のような 

**共有オブジェクト** を複数のスレッドから

同時にアクセスする場合、

**スレッドセーフを考慮する必要** があります。


例えば、`state_event` は

スレッド間で共有されているため、

複数のスレッドが同時にアクセスして

競合しないように注意が必要です。



---



### インスタンスメソッドを呼び出すと、

メソッドのスコープがスタックに積み上がる?


はい、基本的にはその理解で合っています。


- インスタンスメソッドの呼び出しでは、


**前のメソッドのスコープがメモリ上に残ったまま

次のメソッドが呼び出される** ため、

再帰呼び出しや長い処理のネストがあると、

スタックの使用量が増えます。



- しかし、スレッドを分けると 


**別のスレッドのスタックに積まれる** ため、

**スレッドごとに独立して処理される** という違いがあります。


例えば、サンプル02では:


1. `func01()` 内で `notify_state_change()` を呼ぶ。


2. `notify_state_change()` が 

`state_monitoring()` のループで検出される。


3. `state_monitoring()` が 

`func02()` を**別スレッド**で呼び出す。


このとき、`func01()` のスタックと 

`func02()` のスタックは 

**別のスレッドで管理される** ため、

それぞれの処理が独立します。


---



### 結論


1. **処理が完全に独立しているならスレッドを分けるのが有効**  


   → 例: `func01()` が `func02()` に

   影響を与えずに並列処理できるなら、

   スレッドを分けることでパフォーマンス向上の可能性あり。


2. **シンプルな処理ならスレッドを分ける必要なし**  


   → 例: `func01()` の後に `func02()` を

   **順番に**実行するなら、スレッドを分けるよりも

   **シングルスレッドで呼び出すほうがメモリ効率が良い**。


3. **スレッドを分けるとメモリ消費は増える** 

 

   → スレッドごとにスタックメモリを消費するため、

大量のスレッドを作るとメモリ負担が増す。


4. **インスタンスメソッド内で別のメソッドを呼ぶと、

[ メソッドのスコープはスタックに積み上がる**  


   → ただし、**別スレッドなら別のスタックで管理される**。


5. **スレッドを分けるならスレッドセーフを考慮**  


   → `res_instance` のような共有オブジェクトに

   

複数のスレッドがアクセスする場合は、

適切な同期処理 (`threading.Lock()` など) を検討する。



このように、スレッドを分けるかどうかは

「処理の独立性」と「メモリ使用量」とのバランスで

判断するのがポイントです。



コメント