从不同线程更新对象的不同属性是否安全?

时间:2018-08-27 14:59:49

标签: c# .net multithreading

考虑以下伪代码。我有一个带有3个属性的类,每个属性都从不同的方法并行填充。

我会从单独的线程中填充同一类实例的不同属性时遇到麻烦吗?我已经设置了.net fiddle,看来它运行正常。

如果此代码将导致线程问题,那么在填充属性时应使用哪种方法锁定Response类的特定实例?

#include <stdio.h>

// Avoid magic numbers
#define ROWS 8
#define COLS 8

int main(){

    int board[ROWS][COLS] = {0};
    int currentRow = 4, currentColumn = 4;
    int cont = 0, moveNumber = 0, i, j;

    // Loop forever
    while (1) {
        printf("Enter a move: ");
        // Make sure user enters a number
        while (1 != scanf("%d", &moveNumber)) {
            // clear stdin
            int c;
            while((c = getchar()) != '\n' && c != EOF);
            // See https://stackoverflow.com/a/6277391/669576
            // for why fgets/sscanf is a better option than scanf

            // Prompt user for new input
            printf("Enter a valid integer:");
        }
        // Moved this here
        if (moveNumber < 0 || moveNumber > 7) break;
        cont++;
        // Going to use some temp vars to calculate indices
        int tempRow, tempCol;
        // Calc new indices
        switch (moveNumber) {
            case 0:
                tempRow = currentRow - 1;
                tempCol = currentColumn + 2;
                break;
            case 1:
                tempRow = currentRow - 2;
                tempCol = currentColumn + 1;
                break;
            case 2:
                tempRow = currentRow - 2;
                tempCol = currentColumn - 1;
                break;
            case 3:
                tempRow = currentRow - 1;
                tempCol = currentColumn - 2;
                break;
            case 4:
                tempRow = currentRow + 1;
                tempCol = currentColumn - 2;
                break;
            case 5:
                tempRow = currentRow + 2;
                tempCol = currentColumn - 1;
                break;
            case 6:
                tempRow = currentRow + 2;
                tempCol = currentColumn + 1;
                break;
            case 7:
                tempRow = currentRow + 1;
                tempCol = currentColumn + 2;
                break;
        }
        // Make sure we have valid indices
        if (tempRow < 0 || tempCol < 0 || tempRow >= ROWS || tempCol >= COLS) {
            printf("Illegal move\n");
        }
        else {
           // Update the board
           currentRow = tempRow;
           currentColumn = tempCol;
           board[currentRow][currentColumn] = 1;
           // And print
           for(i = 0; i < ROWS; i++){
               for(j = 0; j < COLS; j++){
                   printf("%d ", board[i][j]);
               }
               printf("\n");
           }
           printf("Total moves: %d\n", cont);
        }
    }

    return 0;
}

2 个答案:

答案 0 :(得分:4)

由于只有一个类的实例(保存在response局部变量中),因此没有什么可以锁定在类级别上。在response变量传递给这些任务时,会隐式捕获一个闭包,但这只是堆内存中实例的 pointer ,而不是对象本身。

由于每个自动属性的后备字段都是独立的(保存在类实例中的单独内存位置中),并且每个方法都在更新单独的属性(因此也就是单独的字段),因此没有更新冲突。这些支持字段不是相互依赖的。

换句话说,没有两个任务试图同时更新相同的内存位置,因此此代码是线程安全的。

但是,如果您有多个任务可能同时更新 same 项目,则 不是线程安全的,则必须实现锁定机制或将数据存储在本质上是线程安全的对象(例如ConcurrentDictionary等)中。

答案 1 :(得分:2)

我建议你这样走:

var response = Task<Response>
                    .Factory
                    .ContinueWhenAll(new Task<string>[]{task1, task2, task3},
                                            tasks => new Response {
                                                                    Response1 = tasks[0].Result, 
                                                                    Response2 = tasks[1].Result, 
                                                                    Response3 = tasks[2].Result
                                                                    })
                    .Result;

https://dotnetfiddle.net/LHUa6G