2013-05-27 27 views
7

Dưới đây là mẫu mã cho sử dụng fflush():Hiểu nhu cầu fflush() và các vấn đề liên kết với nó

#include <string.h> 
#include <stdio.h> 
#include <conio.h> 
#include <io.h> 

void flush(FILE *stream); 

int main(void) 
{ 
    FILE *stream; 
    char msg[] = "This is a test"; 

    /* create a file */ 
    stream = fopen("DUMMY.FIL", "w"); 

    /* write some data to the file */ 
    fwrite(msg, strlen(msg), 1, stream); 

    clrscr(); 
    printf("Press any key to flush DUMMY.FIL:"); 
    getch(); 

    /* flush the data to DUMMY.FIL without closing it */ 
    flush(stream); 

    printf("\nFile was flushed, Press any key to quit:"); 
    getch(); 
    return 0; 
} 

void flush(FILE *stream) 
{ 
    int duphandle; 

    /* flush the stream's internal buffer */ 
    fflush(stream); 

    /* make a duplicate file handle */ 
    duphandle = dup(fileno(stream)); 

    /* close the duplicate handle to flush the DOS buffer */ 
    close(duphandle); 
} 

Tất cả tôi biết về fflush() là nó là một chức năng thư viện sử dụng để tuôn một bộ đệm đầu ra. Tôi muốn biết mục đích cơ bản của việc sử dụng fflush() là gì và tôi có thể sử dụng nó ở đâu. Và chủ yếu là tôi quan tâm đến việc biết những vấn đề gì có thể có bằng cách sử dụng fflush().

+0

khi bạn sử dụng 'printf()' mà không cần bất kỳ dòng sản phẩm mới, nó sẽ có thể sẽ không in ngay lập tức. nếu bạn biết chương trình của bạn có thể bị hỏng bất kỳ giây nào, bạn có thể sử dụng 'fflush()' (hoặc newlines ...). Tôi không nghĩ rằng đây là cách sử dụng phổ biến mặc dù. – Elazar

+0

Chỉ có vấn đề tôi có thể nghĩ là làm việc đó quá thường xuyên là xấu cho hiệu suất. Ngoài ra, bạn nên biết rằng việc đóng một tập tin hoặc chương trình chấm dứt ngụ ý một tuôn ra tự động. Một chương trình chạy ngắn sẽ hiếm khi cần gọi 'fflush()' một cách rõ ràng. – thejh

+0

Toàn bộ mục đích của 'stdio' là cung cấp đệm bên ứng dụng cho các cuộc gọi hệ thống' read() 'và' write() '. Bất cứ lúc nào bạn có bộ đệm bạn cần một hoạt động tuôn ra. 'Vấn đề' có thể nhận dạng duy nhất với nó là quên sử dụng nó khi bạn cần, hoặc có thể lạm dụng nó, ví dụ: bên trong vòng thay vì ở cuối vòng lặp. – EJP

Trả lời

34

Đó là một chút khó khăn để nói những gì "có thể là vấn đề với" (quá mức?) Sử dụng fflush. Tất cả các loại sự việc có thể trở thành các vấn đề, tùy thuộc vào mục tiêu và cách tiếp cận của bạn. Có lẽ cách tốt hơn để xem đây là mục đích của fflush là gì.

Điều đầu tiên cần xem xét là fflush chỉ được xác định trên luồng đầu ra. Một luồng đầu ra thu thập "những thứ cần ghi vào một tệp" vào một bộ đệm lớn (ish), và sau đó ghi bộ đệm đó vào tệp. Điểm thu thập và viết sau này là cải thiện tốc độ/hiệu quả, theo hai cách:

  • Trên hệ điều hành hiện đại, có một số hình phạt để vượt qua ranh giới bảo vệ người dùng/hạt nhân (hệ thống phải thay đổi một số thông tin bảo vệ trong CPU, vv). Nếu bạn thực hiện một số lượng lớn các cuộc gọi viết hệ điều hành, bạn sẽ trả tiền phạt đó cho mỗi cuộc gọi. Nếu bạn thu thập, nói, 8192 hoặc để cá nhân viết vào một bộ đệm lớn và sau đó thực hiện một cuộc gọi, bạn loại bỏ hầu hết các chi phí đó.
  • Trên nhiều hệ điều hành hiện đại, mỗi cuộc gọi viết OS sẽ cố gắng tối ưu hóa hiệu suất tệp theo một cách nào đó, ví dụ: bằng cách phát hiện bạn đã mở rộng tệp ngắn sang tệp dài hơn và sẽ tốt hơn nếu di chuyển khối đĩa từ điểm A trên đĩa để trỏ B trên đĩa, sao cho dữ liệu dài hơn có thể vừa vặn với nhau. (Trên các hệ điều hành cũ hơn, đây là một bước "chống phân mảnh" riêng biệt mà bạn có thể chạy thủ công. Bạn có thể nghĩ đây là hệ điều hành hiện đại làm việc phân mảnh tức thời, năng động.) Nếu bạn viết 500 byte và 200 khác, và sau đó là 700, v.v., nó sẽ thực hiện rất nhiều công việc này; nhưng nếu bạn thực hiện một cuộc gọi lớn với, 8192 byte, hệ điều hành có thể phân bổ một khối lớn một lần và đặt mọi thứ ở đó và không cần phải chống phân mảnh lại sau.

Vì vậy, những người cung cấp thư viện C và triển khai luồng stdio của bạn thực hiện bất kỳ điều gì phù hợp trên hệ điều hành của bạn để tìm kích thước khối "hợp lý tối ưu" và thu thập tất cả đầu ra vào đoạn đó. (Các con số 4096, 8192, 16384 và 65536 thường, ngày nay, có xu hướng là những cái tốt, nhưng nó thực sự phụ thuộc vào hệ điều hành và đôi khi cũng là hệ thống tệp cơ bản. Lưu ý rằng "lớn hơn" không phải luôn luôn "tốt hơn": dữ liệu luồng theo khối bốn gigabyte tại một thời điểm có thể sẽ hoạt động kém hơn so với thực hiện theo khối 64 Kbytes chẳng hạn.)

Nhưng điều này tạo ra sự cố. Giả sử bạn đang ghi vào một tệp, chẳng hạn như tệp nhật ký có dấu và thông báo ngày và giờ và mã của bạn sẽ tiếp tục ghi vào tệp đó sau, nhưng ngay bây giờ, nó muốn tạm ngưng một lúc và để bộ phân tích nhật ký đọc nội dung hiện tại của tệp nhật ký. Một tùy chọn là sử dụng fclose để đóng tệp nhật ký, sau đó fopen để mở lại tệp để thêm dữ liệu sau này.Tuy nhiên, hiệu quả hơn là đẩy mọi thông điệp tường trình đang chờ xử lý vào tệp hệ điều hành cơ sở, nhưng giữ cho tệp mở. Đó là những gì fflush làm.

Việc đệm cũng tạo ra một vấn đề khác. Giả sử mã của bạn có một số lỗi và đôi khi nó bị lỗi nhưng bạn không chắc liệu mã đó có bị lỗi hay không. Và giả sử bạn đã viết điều gì đó và điều quan trọng là dữ liệu này sẽ được chuyển tới hệ thống tệp cơ bản. Bạn có thể gọi fflush để đẩy dữ liệu qua hệ điều hành, trước khi gọi mã có khả năng xấu của bạn có thể gặp sự cố. (Đôi khi điều này là tốt để gỡ lỗi.)

Hoặc giả sử bạn đang sử dụng hệ thống giống Unix và có cuộc gọi hệ thống fork. Cuộc gọi này sao chép toàn bộ không gian người dùng (tạo bản sao của quá trình gốc). Bộ đệm stdio nằm trong không gian người dùng, do đó bản sao có cùng dữ liệu đệm nhưng chưa được viết mà quá trình gốc đã có, tại thời điểm cuộc gọi fork. Ở đây một lần nữa, một cách để giải quyết vấn đề là sử dụng fflush để đẩy dữ liệu đệm ra ngay trước khi thực hiện fork. Nếu mọi thứ đã hết trước fork, sẽ không có gì để sao chép; bản sao mới sẽ không bao giờ cố gắng ghi dữ liệu đệm, vì nó không còn tồn tại nữa.

Bạn càng thêm nhiều fflush, bạn càng đánh bại ý tưởng ban đầu về việc thu thập các khối dữ liệu lớn. Tức là, bạn đang cân bằng: các khối lớn hiệu quả hơn, nhưng đang gây ra một số vấn đề khác, vì vậy bạn đưa ra quyết định: "kém hiệu quả ở đây, để giải quyết vấn đề quan trọng hơn hiệu quả đơn thuần". Bạn gọi fflush.

Đôi khi vấn đề chỉ đơn giản là "gỡ lỗi phần mềm". Trong trường hợp đó, thay vì liên tục gọi fflush, bạn có thể sử dụng các chức năng như setbufsetvbuf để thay đổi hành vi đệm của luồng stdio. Điều này thuận tiện hơn (ít hoặc thậm chí không cần thay đổi mã — bạn có thể kiểm soát cuộc gọi đệm bằng cờ) hơn là thêm nhiều cuộc gọi fflush, do đó có thể được coi là "sự cố khi sử dụng (hoặc sử dụng quá mức)) của fflush ".

-1

fflush() làm trống bộ đệm liên quan đến luồng. nếu bạn cho phép người dùng nhập dữ liệu vào một khoảng thời gian rất dài (mili giây) và viết một số nội dung vào một tệp, bộ đệm ghi và đọc có thể có một số "phần còn lại" còn lại trong chính chúng. bạn gọi fflush() sau đó để trống tất cả các bộ đệm và lực lượng đầu ra tiêu chuẩn để đảm bảo đầu vào tiếp theo bạn nhận được là những gì người dùng nhấn sau đó.

tham khảo: http://www.cplusplus.com/reference/cstdio/fflush/

+0

điều gì có thể là vấn đề khi sử dụng? –

+0

'fflush()' là một hàm C chuẩn và * được * hỗ trợ trên Linux. –

+1

Câu trả lời này hoàn toàn sai và gợi ý rằng 'fflush' có thể được sử dụng cho đầu vào. Nó không thể. –

1

Vâng, câu trả lời của @ torek gần như hoàn hảo, nhưng có một điểm không chính xác lắm.

Điều đầu tiên cần xem xét là fflush chỉ được xác định trên luồng đầu ra .

Theo người đàn ông fflush, fflush cũng có thể được sử dụng trong đầu vào suối:

Đối với dòng đầu ra, fflush() buộc một ghi của tất cả dữ liệu đệm sử dụng không gian cho đầu ra cho hoặc cập nhật luồng thông qua chức năng viết cơ bản của luồng. Đối với luồng đầu vào, fflush() loại bỏ mọi dữ liệu đệm đã được tìm nạp từ tệp cơ bản, nhưng chưa được sử dụng bởi ứng dụng. Trạng thái mở của luồng không bị ảnh hưởng. Vì vậy, khi được sử dụng trong đầu vào, fflush chỉ cần loại bỏ nó.

Đây là một bản demo để minh họa cho nó:

#include<stdio.h> 

#define MAXLINE 1024 

int main(void) { 
    char buf[MAXLINE]; 

    printf("prompt: "); 
    while (fgets(buf, MAXLINE, stdin) != NULL) 
    fflush(stdin); 
    if (fputs(buf, stdout) == EOF) 
     printf("output err"); 

    exit(0); 
} 
+6

Sau đó trong cùng một trang người đàn ông, nó nói: "Các tiêu chuẩn không xác định hành vi cho luồng đầu vào. Hầu hết các triển khai khác hoạt động giống như Linux. " –

+0

Tôi cũng lưu ý rằng phần mở rộng Linux là một điều tốt để có thể làm, nhưng trong BSD, chúng tôi đặt nó vào hàm 'fpurge' không chuẩn, được định nghĩa trên cả hai luồng đầu vào và đầu ra. – torek

Các vấn đề liên quan