質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Windows Forms

Windows Forms(WinForms)はMicrosoft .NET フレームワークに含まれる視覚的なアプリケーションのプログラミングインターフェイス(API)です。WinFormsは管理されているコードの既存のWindowsのAPIをラップすることで元のMicrosoft Windowsのインターフェイスのエレメントにアクセスすることができます。

Q&A

解決済

1回答

991閲覧

Task.Factory.StartNew(() => {})の中で変数が勝手に書き換わってしまう原因が知りたい。

退会済みユーザー

退会済みユーザー

総合スコア0

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Windows Forms

Windows Forms(WinForms)はMicrosoft .NET フレームワークに含まれる視覚的なアプリケーションのプログラミングインターフェイス(API)です。WinFormsは管理されているコードの既存のWindowsのAPIをラップすることで元のMicrosoft Windowsのインターフェイスのエレメントにアクセスすることができます。

0グッド

0クリップ

投稿2022/07/29 06:46

提示コードの///コメント部内のコードですがfor文のループ変数jが勝手に書き換わってしまいます、コンソールログ参照。これはなぜでしょうか?filePathList.Count の値は確かになのですが、書き換わる原因が知りたいです。

コンソールログ
ああああ 1 1 うううう 0 いういいい 1 例外がスローされました: 'System.ArgumentOutOfRangeException' (System.Private.CoreLib.dll の中) 型 'System.ArgumentOutOfRangeException' の例外が System.Private.CoreLib.dll で発生しましたが、ユーザー コード内ではハンドルされませんでした Index was out of range. Must be non-negative and less than the size of the collection.
ソースコード

cs

1 2 private async void buttonConvert_Click(object sender, EventArgs e) 3 { 4 if(folderBrowserDialogSavePath.SelectedPath != "") 5 { 6 if(filePathList.Count > 0) 7 { 8 Debug.WriteLine("ああああ"); 9 for (int i = 0; i < filePathList.Count; i++) 10 { 11 string file = Path.GetFileName(filePathList[i]); 12 file = Path.ChangeExtension(file,".jpeg"); 13 file = folderBrowserDialogSavePath.SelectedPath + "\\" + file; 14 filePathList_Save.Add(file); 15 } 16 17 buttonConvert.Enabled = false; 18 Stopwatch sw = new Stopwatch(); 19 sw.Start(); 20 var slim = new SemaphoreSlim(5); 21 22 23 Debug.WriteLine(filePathList.Count); 24 Debug.WriteLine(filePathList_Save.Count); 25 26////////////////////////////////////////////////////////////////////////////////////////////// 27 for(int j = 0; j < filePathList.Count; j++) 28 { 29 Debug.WriteLine("うううう " + j); 30 31 taskList.Add(Task.Factory.StartNew(() => 32 { 33 Debug.WriteLine("いういいい " + j); 34 Process(slim, filePathList[j],filePathList_Save[j]); 35 this.Invoke((Action)(() => 36 { 37 progressBar_float += progressBarPerFile; 38 progressBar.Value = (int)progressBar_float; 39 })); 40 })); 41 } 42////////////////////////////////////////////////////////////////////////////////////////////// 43 await Task.WhenAll(taskList); 44 buttonConvert.Enabled = true; 45 46 slim.Dispose(); 47 progressBar.Value = 0; 48 sw.Stop(); 49 TimeSpan ts = sw.Elapsed; 50 51 Debug.WriteLine("convert time: " + ts.TotalSeconds +"."+ ts.Milliseconds); 52 } 53 else 54 { 55 MessageBox.Show("NO input file(s)", "", MessageBoxButtons.OK, MessageBoxIcon.Error); 56 } 57 } 58 else 59 { 60 MessageBox.Show("\n\n NO set save path", "", MessageBoxButtons.OK, MessageBoxIcon.Error); 61 } 62 63 }

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

YAmaGNZ

2022/07/29 07:03

非同期で実行されているからでは?
kikukiku

2022/07/29 07:23

jのインスタンスは下記で作成されています。 for(int j = 0; j < filePathList.Count; j++) Debug.WriteLine("いういいい " + j);にて 複数存在する別スレッドから共通同じインスタンスjにアクセスしています。 別スレッドが動作中も、UIスレッド上のbuttonConvert_Clickは動作し続けるので jはカウントアップされます。 別スレッドの動作するタイミングによって、 変数jが1のときにjを参照すれば1になるし、 変数jが2のときにjを参照すれば2になるということ。 なので下記のように変数kのインスタンスを毎回作成してあげれば 別スレッドから個別のインスタンスkを参照するようになるので 期待する動作をするのではないか。 Debug.WriteLine("うううう " + j); var k = j; taskList.Add(Task.Factory.StartNew(() => { Debug.WriteLine("いういいい " + k);
退会済みユーザー

退会済みユーザー

2022/07/30 03:55

質問ですが "別スレッドの動作するタイミングによって、"についてもうちょっと詳しく聞いてもいいですか? "buttonConvert_Clickが動作し続ける"というところの意味がわかりません。
kikukiku

2022/07/31 23:51

うーん、上記の文章でわからないとなると、 図を書いて説明が必要なのかもですが 申し訳ないのですが、面倒すぎて説明の気力が湧きません。 別スレッドの起動ですが、別スレッドを起動する命令が実行されても 実際にはすぐ実行されるのか、時間をおいてから実行されるのかは 保証がないため、早く実行されれば、カウントが1になることもあるし、 遅く実行されれば、カウントが2になることもあるでしょう。
guest

回答1

0

ベストアンサー

変数が書き換わるのは、コメント欄で kikukiku さんが書いてらっしゃるように、それぞれのタスクが同じ変数を参照しているからです。
このような場合は、StartNew(Action<Object>, Object) を使って Process の起動に必要なパラメタを渡すといいと思います。
下の例では object[] に入れてますが、構造体、クラス、タプル、あるいは匿名クラスを使うのも良いでしょう。
(インデックス j のみ渡してもOK)

C#

1 (略) 2// Action<object> を定義 3Action<object> action = (object obj) => { 4 object[] args = (object[])obj; 5 Process((SemaphoreSlim)args[0], (string)args[1], (string)args[2]); 6}; 7 8for (int j = 0; j < filePathList.Count; j++) { 9 taskList.Add(Task.Factory.StartNew(action, 10 new object[] { slim, filePathList[j], filePathList_Save[j] })); 11 (略)

NET6 の場合は

C#

1// Action<object?> を定義 2Action<object?> action = (object? obj) => { 3 if (obj is not object[] args) { 4 throw new ArgumentNullException(nameof(obj)); 5 } 6 Process((SemaphoreSlim)args[0], (string)args[1], (string)args[2]); 7};

for を foreach に変えてもOK

C#

1for(int j = 0; j < filePathList.Count; j++) 23foreach (int j in Enumerable.Range(0, filePathList.Count))

投稿2022/07/30 03:18

編集2022/07/30 06:02
KOZ6.0

総合スコア2628

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2022/07/30 03:58

質問ですが以下の警告が出るのですがこれはどういう意味なのでしょうか? [ 重大度レベル コード 説明 プロジェクト ファイル 行 抑制状態 警告 CS8620 型 'Action<object>' の引数は、参照型の NULL 値の許容の違いにより、'Task TaskFactory.StartNew(Action<object?> action, object? state)' の型 'Action<object?>' のパラメーター 'action' には使用できません。 HEIC_Converter C:\Users\yw325\Desktop\HEIC_Converter\HEIC_Converter\Main.cs 222 アクティブ ]
KOZ6.0

2022/07/30 05:10 編集

あ、NET6 ですか。 見たまんまです。Action<object> を Action<object?> に変えてください。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問