C# - 线程间操作无效,无法实现回溯



我尝试创建一个数独解析器。因此,在业务逻辑中,我们有一个代表数独的游戏板。在业务逻辑中,我们使用一种执行回溯处理的方法来解决数独问题。

我在 Form1 中的通知更改方法中遇到以下错误.cs: tableLayoutPanel1.Controls.Add(label, column, line);

线程

间操作 没有从创建它的线程以外的线程访问的有效控件

数独听众.cs :

interface SudokuListener
{
    void NotifyChange(Int32 line, Int32 column, Token token);
}

表格1 :

public partial class Form1 : Form, SudokuListener
{
    delegate void Del(SudokuListener sudokuListener);
    void SudokuListener.NotifyChange(int line, int column, Token token)
    {
        Label label = new Label();
        if (token.IsMarked())
        {
            label.ForeColor = Color.Blue;
        }
        label.Text = ""+token.GetNbr();
        tableLayoutPanel1.Controls.Add(label, column, line);
    }
    private void Form1_Load(object sender, EventArgs e)
    {
        Del del = new Del(SudokuThread.Start);
        BackgroundWorker backgroundWorker = new BackgroundWorker();
        backgroundWorker.WorkerReportsProgress = true;
        backgroundWorker.WorkerSupportsCancellation = true;
        backgroundWorker.DoWork += new DoWorkEventHandler(brol);
        backgroundWorker.RunWorkerAsync();
    }
    private void brol(object sender, DoWorkEventArgs e)
    {
        SudokuThread.Start(this);
    }
}

数独线程.cs :

class SudokuThread
{
    public static void Start(SudokuListener listener)
    {
        SudokuGameBoard sudokuGameBoard = new SudokuGameBoard();
        Sudoku sudoku = new Sudoku();
        sudokuGameBoard.AddListeners(listener);
        sudoku.Load(sudokuGameBoard);
        sudoku.FindSolution(sudokuGameBoard, 0);
    }
}

数独.cs :

class Sudoku
{
    public void Load(SudokuGameBoard gameBoard)
    {
        gameBoard.SetToken(0, 0, new Token(0, false));
        gameBoard.SetToken(0, 1, new Token(0, false));
        gameBoard.SetToken(0, 2, new Token(0, false));
        gameBoard.SetToken(0, 3, new Token(0, false));
        gameBoard.SetToken(0, 4, new Token(0, false));
        gameBoard.SetToken(0, 5, new Token(0, false));
        gameBoard.SetToken(0, 6, new Token(0, false));
        gameBoard.SetToken(0, 7, new Token(0, false));
        gameBoard.SetToken(0, 8, new Token(0, false));
        gameBoard.SetToken(1, 0, new Token(0, false));
        gameBoard.SetToken(1, 1, new Token(0, false));
        gameBoard.SetToken(1, 2, new Token(7, true));
        gameBoard.SetToken(1, 3, new Token(8, true));
        //rest of the code
    }
    private void PutToken(SudokuGameBoard gameBoard, Int32 line, Int32 column, Token token)
    {
        if ( !gameBoard.GetToken(line,column).IsMarked())
        {
            gameBoard.SetToken(line, column, token);
        }
    }
    public Boolean FindSolution(SudokuGameBoard gameBoard, Int32 position)
    {
        Boolean finish = false;
        Int32 currentNbr = 0;
        do {
            currentNbr++;
            if ( IsPossible(gameBoard, position / 9, position % 9, currentNbr) )
            {
                PutToken(gameBoard, position / 9, position % 9, new Token(currentNbr));
                //rest of the code
            }
       }while (currentNbr != 9 && !finish);
    }
}

数独游戏板.cs :

class SudokuGameBoard
{
    public void SetToken(Int32 line, Int32 column, Token newToken)
    {
        array[line, column] = newToken;
        NotifyListeners(line, column, newToken);
    }
    private void NotifyListeners(Int32 line, Int32 column, Token token)
    {
        for (Int32 i = 0; i < listeners.Count; i++)
        {
            listeners[i].NotifyChange(line, column, token);
        }
    }
  }

导致错误本身是因为无法从非 UI 线程修改 UI。这不是 .NET 或 Windows 的限制。没有窗口系统允许后台线程修改 UI。

您的代码也过于复杂,使用充当事件的委托和后台工作者。BGW 已经过时,因为它所做的任何事情都可以通过任务和用于报告的 IProgress 接口轻松完成。任务允许您轻松执行多个后台操作,或在后台操作后返回到 UI 线程。对于BGW来说,这两件事都非常困难。

您的代码可以像以下代码一样简单:

报告讨论区状态的类

class BoardStatus
{
    public int Line {get;}
    public int Column {get;}
    public Token Token {get;}
    public BoardStatus(int line, int column,Token token)
    {
        Line=line;
        Column=column;
        this.Token=token;
    }
}

以及以下代码的格式:

void UpdateStatus(BoardStatus status)
{
    var color = status.token.IsMarked() ? Color.Blue : Color.Black;
    var label = new Label
                {
                    Text = token.GetNbr().ToString(),
                    ForeColor = color;
                };
    tableLayoutPanel1.Controls.Add(label, status.column, status.line);
}
public async void Start_Click(object sender, EventArgs args)
{
    TotalStatusLabel.Text="Starting";
    var progress=new Progress<TokenProgress>(UpdateStatus);
    //Run in the background without blocking
    await Task.Run(()=>Solve(progress));
    //We are back in the UI
    TotalStatusLabel.Text="Finished";
}
private void Solve(IProgress<TokenStatus> progress)
{
    SudokuGameBoard sudokuGameBoard = new SudokuGameBoard(progress);
    Sudoku sudoku = new Sudoku();
    sudoku.Load(sudokuGameBoard);
    sudoku.FindSolution(sudokuGameBoard, 0);
}

随着董事会通过 IProgress 界面报告进度:

class SudokuGameBoard
{
    IProgress<BoardStatus> _progress;
    public SudokuGameBoard(IProgress<BoardStatus> progress)
    {
        _progress=progress;
    }
    public void SetToken(Int32 line, Int32 column, Token newToken)
    {
        array[line, column] = newToken;
        _progress.Report(new BoardStatus(line, column, newToken));
    } 
}

最新更新