线程是否可以运行由分配给它的方法调用的递归?

时间:2016-05-19 20:08:46

标签: c# multithreading recursion unity3d

这可能是一个愚蠢的问题,我暂时找不到答案。

基本上:我有一个方法创建一个调用一个调用递归的方法的线程,....这个递归运行吗?因为我得到的是垃圾...... 我用c#代码为unity3d编写代码。我想在单独的线程上运行的脚本不包含统一api方法。

精化:

这就是我所拥有的:

方法GetPCNextTurn创建如下的线程:

Thread myThread = new Thread(() => CompPlayTurn(MinMaxBoard, weights));
myThread.Start();

然后CompPlayTurn应该开始吧?

CompPlayTurn调用ScoreBoard,返回一个值。

然后在一些条件CompPlayTurn调用递归后,再次调用ScoreBoard

我会假设此时它会在线程起始行之后返回第一个方法吗?

似乎并没有像我希望的那样发生。有人可以用线程和递归的行为来启发我吗?

我需要1个线程进行此递归,我需要的是将它与主线程分开。

这是代码: 这是脚本中的主要方法:

  public int GetPCNextTurn(int[][] board, int height, int width, int sequence)
    {
        this.done = false;
        this.height = height;
        this.width = width;
        this.sequence = sequence;

        int[][] MinMaxBoard = CopyBoard(board);

        weights = GetWeights(sequence);

        Thread myThread = new Thread(() => CompPlayTurn(MinMaxBoard, weights));
        myThread.Start();

        return ans;
    }

public void CompPlayTurn(int[][] MinMaxBoard, int[] weights)      
    {        
        int scoreOrig = ScoreBoard(MinMaxBoard);

        if (scoreOrig == orangeWins) winner = (int)Winner.pc;
        //     Debug.Log("I win\n");

        else if (scoreOrig == yellowWins) winner = (int)Winner.player;
           // Debug.Log("You win\n");

        else
        {
            int move, score;

            Minimax(true, (int)Mycell.Orange, maxDepth, MinMaxBoard, out move, out score);
            ans = move;

            if (move != -1)
            {
                ans = move;
                //    dropDisk(board, move, (int)Mycell.Orange);

                scoreOrig = ScoreBoard(MinMaxBoard);
                if (scoreOrig == orangeWins) { winner = (int)Winner.pc; }//Debug.Log("I win\n"); }
                else if (scoreOrig == yellowWins) { winner = (int)Winner.player; }//Debug.Log("You win\n"); }

            }
            else winner = (int)Winner.draw;
        }
    }

 public int ScoreBoard(int[][] scores)
    {
        int[] counters;
        int x, y, count = 0, size = (2 * sequence + 1);
        counters = new int[size];

        Array.Clear(counters, 0, counters.Length); //needed?

        // Horizontal spans
        for (y = 0; y < height; y++)
        {
            int score = 0;
            for (int i = 0; i <= sequence - 2; i++)
                score += scores[y][i];

            for (x = (sequence - 1); x < width; x++)
            {
                score += scores[y][x];
                counters[score + sequence]++;
                score -= scores[y][x - (sequence - 1)];
            }
        }

        // Vertical spans
        for (x = 0; x < width; x++)
        {
            int score = 0;
            for (int i = 0; i <= sequence - 2; i++)
                score += scores[i][x];

            for (y = (sequence - 1); y < height; y++)
            {
                score += scores[y][x];
                counters[score + sequence]++;
                score -= scores[y - (sequence - 1)][x];
            }
        }
        // Down-right (and up-left) diagonals
        for (y = 0; y < height - (sequence - 1); y++)
        {
            for (x = 0; x < width - (sequence - 1); x++)
            {
                int score = 0, idx = 0;
                for (idx = 0; idx < sequence; idx++)
                {
                    score += scores[y + idx][x + idx];
                }
                counters[(score + sequence)]++;
            }
        }
        // up-right (and down-left) diagonals
        for (y = (sequence - 1); y < height; y++)
        {
            for (x = 0; x < width - (sequence - 1); x++)
            {
                int score = 0, idx = 0;
                for (idx = 0; idx < sequence; idx++)
                {
                    score += scores[y - idx][x + idx];
                }
                counters[(score + sequence)]++;
            }
        }
        if (counters[0] != 0)
            return yellowWins;
        else if (counters[(sequence * 2)] != 0)
            return orangeWins;


        else
        {

            for (int i = 1; i < size - 1; i++)
            { count += weights[i] * counters[i]; }

            return count;
        }

    }

 public void Minimax(bool maximizeOrMinimize, int color, int depth, int[][] MinMaxBoard, out int move, out int score)
    {
        if (0 == depth)
        {
            move = -1;

            score = ScoreBoard(MinMaxBoard);
        }
        else
        {               
            int bestScore = maximizeOrMinimize ? -10000000 : 10000000;
            int bestMove = -1;


            for (int column = 0; column < width; column++)
            {
                if (MinMaxBoard[0][column] != (int)Mycell.Barren)
                    continue;
                int rowFilled = dropDisk(MinMaxBoard, column, color); // damage the state

                if (rowFilled == -1)
                    continue;
                int s = ScoreBoard(MinMaxBoard);

                if (s == (maximizeOrMinimize ? orangeWins : yellowWins))
                {
                    bestMove = column;
                    bestScore = s;
                    MinMaxBoard[rowFilled][column] = (int)Mycell.Barren;
                    break;
                }

                int moveInner, scoreInner;
                Minimax(!maximizeOrMinimize, color == (int)Mycell.Orange ? (int)Mycell.Yellow : (int)Mycell.Orange,
                depth - 1, MinMaxBoard, out moveInner, out scoreInner);

                MinMaxBoard[rowFilled][column] = (int)Mycell.Barren; // Undo the damage

                // No need for lists and sorting - just keep the best value you meet.
                if (maximizeOrMinimize)
                {
                    if (scoreInner >= bestScore)
                    {
                        bestScore = scoreInner;
                        bestMove = column;
                    }
                }
                else
                {
                    if (scoreInner <= bestScore)
                    {
                        bestScore = scoreInner;
                        bestMove = column;
                    }
                }

            }

            move = bestMove;
            score = bestScore;
        }
    }
  public int dropDisk(int[][] MinMaxBoard, int column, int color)
    {
        int y;
        for (y = height - 1; y >= 0; y--)
            if (MinMaxBoard[y][column] == (int)Mycell.Barren)
            {
                MinMaxBoard[y][column] = color;
                return y;
            }
        return -1;
    }

编辑:

我尝试添加一个方法来判断线程是否已完成运行:

    public bool TryGetValue(out int val)
    {

        val = ans;
        this.done = false;  
        if (done==true)
            return true;

        return false; 
    }

线程有一个公共bool变量,它在CompPlayTurn中被初始化,所以线程第一次用false初始化它

只是为了测试它,我在CompPlayTurn内将其值设置为true,这是它做的第一件事(在递归和所有内容之前,就在方法签名之后)。

对于我添加的主线程:

  while (!(minimaxscript.TryGetValue(out column)))
                { StartCoroutine(wait(count)); }

 public IEnumerator wait(int count)
    {
        Debug.Log("not done yet");
        count++;
        if (count == 7)
        {
            Application.Quit();
            yield break;
        }
        yield return new WaitForSeconds(3f);
    }

我开始使用yield return new WaitForEndOfFrame();然后返回new waitforseconds,最后我添加了Application.Quit()

它冻结....我不认为这是线索,因为他没有称之为追索权......可以是主线程逻辑吗?它运行时没有线程递归ai就好了。

我甚至尝试在创建线程(done=true)后立即将bool变量更新为:after the thread.start,因此它应该是在离开脚本之前更新变量的主线程,但它仍然会冻结。好像变量永远不会被设置....但是如果没有这种保护,我会得到价值,它们从何而来呢?。

Nvm只是缺乏睡眠 - .-我很蠢......

这是我想写的方法,当我写下我做的事情时,不知道是什么让我想到了:

  public bool TryGetValue(out int val)
    {            
        val = ans;

        return done;           

}

编辑: 我将done = false更改为thead所做的第一件事(调用方法中的第一行)和done = false,结束时。

然后主线程调用waitforseconds(1f)和调试消息以确保它等待。

使用:

    bool ok = (minimaxscript.TryGetValue(out column));
                while (ok == false)
                {
                    ok = (minimaxscript.TryGetValue(out column));
                  StartCoroutine(  wait());
                }

它冻结了......

最终解决方案:

添加轮询并保护方法不被调用但没有完成但似乎解决了问题。

问题最终是团结一致,我一次又一次地调用这个方法。 我正在检查它是否已完成并对其进行协调,但无论是通过更新方法中调用的方法都保持调用它,所以我没有注意到。

1 个答案:

答案 0 :(得分:1)

要回答你的问题,是的,你可以递归调用一个函数,它将保留在你刚创建的线程中。一旦启动该线程,无论你调用什么函数,它都将保留在该线程上,直到它不再有任何代码要执行。

但是,您似乎要在public function login($username, $password){ session_start(); if (!empty($username) && !empty($password)) { $stmt = $this->db->prepare("SELECT * FROM user WHERE username=? and password=?"); $stmt->bindParam(1, $username); $stmt->bindParam(2, $password); $stmt->execute(); if ($stmt->rowCount() == 1) { $_SESSION['login'] = true; $_SESSION['username'] = $username; } else { echo "Wrong username or password"; } } else { echo "Please enter username and password"; } } 函数中为“ans”分配值,然后从CompPlayTurn

返回该值

问题是你不能保证在你从那个函数返回之前会分配ans(并且很可能不会)。当您创建新线程时,您无法保证每个线程何时完成工作。这意味着可以在返回函数之前或之后设置ans,从而使返回值不再有效。

如果您希望能够从单独的线程返回值,则需要将委托传递给该函数,或者创建一个单独的函数以在设置该值时进行轮询。但是要注意,任何特定于统一的东西只能在主线程中使用,所以我建议使用轮询函数来返回值

GetPCNextTurn

这是一个非常粗略的实现,因为对于任何多线程,您都希望使其更加强大,我主要是写这个来向您展示概念